@usequeek/app-sdk 0.4.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/CHANGELOG.md +120 -0
- package/LICENSE +21 -0
- package/README.md +210 -0
- package/dist/app-auth.d.ts +139 -0
- package/dist/app-auth.d.ts.map +1 -0
- package/dist/app-auth.js +205 -0
- package/dist/app-auth.js.map +1 -0
- package/dist/background.d.ts +68 -0
- package/dist/background.d.ts.map +1 -0
- package/dist/background.js +78 -0
- package/dist/background.js.map +1 -0
- package/dist/client.d.ts +148 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +269 -0
- package/dist/client.js.map +1 -0
- package/dist/collected.d.ts +103 -0
- package/dist/collected.d.ts.map +1 -0
- package/dist/collected.js +59 -0
- package/dist/collected.js.map +1 -0
- package/dist/crypto.d.ts +6 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +76 -0
- package/dist/crypto.js.map +1 -0
- package/dist/frame.d.ts +58 -0
- package/dist/frame.d.ts.map +1 -0
- package/dist/frame.js +56 -0
- package/dist/frame.js.map +1 -0
- package/dist/handoff.d.ts +113 -0
- package/dist/handoff.d.ts.map +1 -0
- package/dist/handoff.js +29 -0
- package/dist/handoff.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/install-handlers.d.ts +62 -0
- package/dist/install-handlers.d.ts.map +1 -0
- package/dist/install-handlers.js +238 -0
- package/dist/install-handlers.js.map +1 -0
- package/dist/logger.d.ts +27 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +75 -0
- package/dist/logger.js.map +1 -0
- package/dist/merchant-schema.d.ts +13963 -0
- package/dist/merchant-schema.d.ts.map +1 -0
- package/dist/merchant-schema.js +11 -0
- package/dist/merchant-schema.js.map +1 -0
- package/dist/resync.d.ts +80 -0
- package/dist/resync.d.ts.map +1 -0
- package/dist/resync.js +203 -0
- package/dist/resync.js.map +1 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +8 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +66 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +155 -0
- package/dist/session.js.map +1 -0
- package/dist/signatures.d.ts +55 -0
- package/dist/signatures.d.ts.map +1 -0
- package/dist/signatures.js +74 -0
- package/dist/signatures.js.map +1 -0
- package/dist/store.d.ts +278 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +610 -0
- package/dist/store.js.map +1 -0
- package/dist/tokens.d.ts +162 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +411 -0
- package/dist/tokens.js.map +1 -0
- package/dist/webhooks.d.ts +60 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +125 -0
- package/dist/webhooks.js.map +1 -0
- package/openapi/merchant.json +21452 -0
- package/package.json +63 -0
package/dist/store.js
ADDED
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { Pool } from "pg";
|
|
3
|
+
import { decryptSecret, encryptSecret, parseStoreKey } from "./crypto.js";
|
|
4
|
+
const SEEN_TTL_SECONDS = 24 * 60 * 60;
|
|
5
|
+
const INSTALLATIONS_TABLE_SQLITE = `
|
|
6
|
+
CREATE TABLE IF NOT EXISTS installations (
|
|
7
|
+
installation_id TEXT PRIMARY KEY,
|
|
8
|
+
installation_pid TEXT NOT NULL,
|
|
9
|
+
vendor_id TEXT NOT NULL,
|
|
10
|
+
store_pid TEXT,
|
|
11
|
+
store_name TEXT NOT NULL,
|
|
12
|
+
api_base TEXT NOT NULL,
|
|
13
|
+
token_enc TEXT,
|
|
14
|
+
token_expires_at TEXT,
|
|
15
|
+
token_kid TEXT,
|
|
16
|
+
pending INTEGER NOT NULL DEFAULT 0,
|
|
17
|
+
scopes_json TEXT NOT NULL,
|
|
18
|
+
settings_json TEXT NOT NULL,
|
|
19
|
+
webhook_secret_enc TEXT,
|
|
20
|
+
proxy_secret_enc TEXT,
|
|
21
|
+
embed_secret_enc TEXT,
|
|
22
|
+
app_id TEXT,
|
|
23
|
+
webhook_url TEXT,
|
|
24
|
+
webhook_topics_json TEXT NOT NULL,
|
|
25
|
+
installed_at TEXT NOT NULL,
|
|
26
|
+
updated_at TEXT NOT NULL
|
|
27
|
+
);
|
|
28
|
+
CREATE TABLE IF NOT EXISTS seen_webhook_ids (
|
|
29
|
+
webhook_id TEXT PRIMARY KEY,
|
|
30
|
+
seen_at INTEGER NOT NULL
|
|
31
|
+
);
|
|
32
|
+
`;
|
|
33
|
+
/**
|
|
34
|
+
* Default local/dev/test `InstallationStore`: SQLite through the Node 22
|
|
35
|
+
* built-in `node:sqlite` (`DatabaseSync` — synchronous, file-backed, WAL
|
|
36
|
+
* mode).
|
|
37
|
+
*
|
|
38
|
+
* Why not better-sqlite3: its prebuilt binary segfaults on the project's
|
|
39
|
+
* Node 22.12 runtime (observed, not theorised), and building from source
|
|
40
|
+
* would force a C++ toolchain into every environment — dev Macs, CI, and
|
|
41
|
+
* the slim alpine app images. `node:sqlite` is dependency-free with an
|
|
42
|
+
* identical synchronous shape.
|
|
43
|
+
*
|
|
44
|
+
* `node:sqlite` needs no flag since Node 22.13.0 (pinned runtime 22.23.3;
|
|
45
|
+
* an ExperimentalWarning on stderr remains — Stability 1.1). Devs on an
|
|
46
|
+
* older 22 minor set `NODE_OPTIONS=--experimental-sqlite` in their shell.
|
|
47
|
+
* Revisit when the apps run on a Node line where it is fully stable — the
|
|
48
|
+
* swap is contained here, behind `InstallationStore`.
|
|
49
|
+
*
|
|
50
|
+
* 0.1.x → 0.2.0 upgrade: databases created by SDK 0.1.x carry a legacy
|
|
51
|
+
* `api_key_enc` column (the handoff no longer delivers that key, so its
|
|
52
|
+
* value is dead — minted tokens replace it; resync restores connectivity).
|
|
53
|
+
* Opening such a database adds the token-cache columns and drops
|
|
54
|
+
* `api_key_enc`; the legacy ciphertext is discarded, never decrypted.
|
|
55
|
+
*/
|
|
56
|
+
export class SqliteInstallationStore {
|
|
57
|
+
db;
|
|
58
|
+
key;
|
|
59
|
+
constructor(options) {
|
|
60
|
+
this.key = parseStoreKey(options.storeKey);
|
|
61
|
+
this.db = new DatabaseSync(options.path);
|
|
62
|
+
this.db.exec("PRAGMA journal_mode = WAL;");
|
|
63
|
+
this.db.exec(INSTALLATIONS_TABLE_SQLITE);
|
|
64
|
+
this.migrateFromApiKeySchema();
|
|
65
|
+
}
|
|
66
|
+
migrateFromApiKeySchema() {
|
|
67
|
+
const columns = this.db.prepare(`PRAGMA table_info(installations)`).all();
|
|
68
|
+
const names = new Set(columns.map((column) => column.name));
|
|
69
|
+
if (names.has("api_key_enc")) {
|
|
70
|
+
for (const column of ["token_enc", "token_expires_at", "token_kid"]) {
|
|
71
|
+
if (!names.has(column))
|
|
72
|
+
this.db.exec(`ALTER TABLE installations ADD COLUMN ${column} TEXT;`);
|
|
73
|
+
}
|
|
74
|
+
// The 0.1.x handoff key is superseded by minted installation tokens:
|
|
75
|
+
// its ciphertext is dropped, never decrypted or re-encrypted.
|
|
76
|
+
this.db.exec(`ALTER TABLE installations DROP COLUMN api_key_enc;`);
|
|
77
|
+
}
|
|
78
|
+
// Schema v2 (B2 review r2): the persisted 409-pending mark. Existing
|
|
79
|
+
// rows backfill to 0 (not pending); fresh tables already carry the
|
|
80
|
+
// column via the CREATE above, so this is a no-op for them.
|
|
81
|
+
const live = new Set(this.db.prepare(`PRAGMA table_info(installations)`).all().map((column) => column.name));
|
|
82
|
+
if (!live.has("pending")) {
|
|
83
|
+
this.db.exec(`ALTER TABLE installations ADD COLUMN pending INTEGER NOT NULL DEFAULT 0;`);
|
|
84
|
+
}
|
|
85
|
+
// Schema v3 (FA1): the installation proxy secret for slot-claim
|
|
86
|
+
// signing. Existing rows backfill to NULL (no proxy secret known
|
|
87
|
+
// until the next install/resync handoff); fresh tables already carry
|
|
88
|
+
// the column via the CREATE above, so this is a no-op for them.
|
|
89
|
+
if (!live.has("proxy_secret_enc")) {
|
|
90
|
+
this.db.exec(`ALTER TABLE installations ADD COLUMN proxy_secret_enc TEXT;`);
|
|
91
|
+
}
|
|
92
|
+
// Schema v4 (S4): the embed secret + app id that verify dashboard
|
|
93
|
+
// session tokens. Existing rows backfill to NULL until the next
|
|
94
|
+
// install/resync handoff delivers them.
|
|
95
|
+
if (!live.has("embed_secret_enc")) {
|
|
96
|
+
this.db.exec(`ALTER TABLE installations ADD COLUMN embed_secret_enc TEXT;`);
|
|
97
|
+
}
|
|
98
|
+
if (!live.has("app_id")) {
|
|
99
|
+
this.db.exec(`ALTER TABLE installations ADD COLUMN app_id TEXT;`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
saveInstallation(record) {
|
|
103
|
+
const now = new Date().toISOString();
|
|
104
|
+
this.db
|
|
105
|
+
.prepare(`INSERT INTO installations (
|
|
106
|
+
installation_id, installation_pid, vendor_id, store_pid, store_name,
|
|
107
|
+
api_base, token_enc, token_expires_at, token_kid, pending,
|
|
108
|
+
scopes_json, settings_json,
|
|
109
|
+
webhook_secret_enc, proxy_secret_enc, embed_secret_enc, app_id, webhook_url, webhook_topics_json,
|
|
110
|
+
installed_at, updated_at
|
|
111
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
112
|
+
ON CONFLICT(installation_id) DO UPDATE SET
|
|
113
|
+
installation_pid = excluded.installation_pid,
|
|
114
|
+
vendor_id = excluded.vendor_id,
|
|
115
|
+
store_pid = excluded.store_pid,
|
|
116
|
+
store_name = excluded.store_name,
|
|
117
|
+
api_base = excluded.api_base,
|
|
118
|
+
token_enc = excluded.token_enc,
|
|
119
|
+
token_expires_at = excluded.token_expires_at,
|
|
120
|
+
token_kid = excluded.token_kid,
|
|
121
|
+
pending = excluded.pending,
|
|
122
|
+
scopes_json = excluded.scopes_json,
|
|
123
|
+
settings_json = excluded.settings_json,
|
|
124
|
+
webhook_secret_enc = excluded.webhook_secret_enc,
|
|
125
|
+
proxy_secret_enc = excluded.proxy_secret_enc,
|
|
126
|
+
embed_secret_enc = excluded.embed_secret_enc,
|
|
127
|
+
app_id = excluded.app_id,
|
|
128
|
+
webhook_url = excluded.webhook_url,
|
|
129
|
+
webhook_topics_json = excluded.webhook_topics_json,
|
|
130
|
+
updated_at = excluded.updated_at`)
|
|
131
|
+
.run(record.installationId, record.installationPid, record.vendorId, record.storePid, record.storeName, record.apiBase, record.token === null ? null : encryptSecret(record.token, this.key), record.tokenExpiresAt, record.tokenKid, record.pending ? 1 : 0, JSON.stringify(record.scopes), encryptSecret(JSON.stringify(record.settings), this.key), record.webhookSecret === null ? null : encryptSecret(record.webhookSecret, this.key), record.proxySecret === null || record.proxySecret === undefined
|
|
132
|
+
? null
|
|
133
|
+
: encryptSecret(record.proxySecret, this.key), record.embedSecret === null || record.embedSecret === undefined
|
|
134
|
+
? null
|
|
135
|
+
: encryptSecret(record.embedSecret, this.key), record.appId ?? null, record.webhookUrl, JSON.stringify(record.webhookTopics), record.installedAt, now);
|
|
136
|
+
}
|
|
137
|
+
getInstallation(installationId) {
|
|
138
|
+
const row = this.db
|
|
139
|
+
.prepare(`SELECT * FROM installations WHERE installation_id = ?`)
|
|
140
|
+
.get(installationId);
|
|
141
|
+
if (!row)
|
|
142
|
+
return null;
|
|
143
|
+
return rowToRecord(row, this.key);
|
|
144
|
+
}
|
|
145
|
+
listInstallations() {
|
|
146
|
+
const rows = this.db.prepare(`SELECT * FROM installations ORDER BY installation_id`).all();
|
|
147
|
+
return rows.map((row) => rowToRecord(row, this.key));
|
|
148
|
+
}
|
|
149
|
+
clearCachedToken(installationId) {
|
|
150
|
+
this.db
|
|
151
|
+
.prepare(`UPDATE installations SET token_enc = NULL, token_expires_at = NULL, token_kid = NULL
|
|
152
|
+
WHERE installation_id = ?`)
|
|
153
|
+
.run(installationId);
|
|
154
|
+
}
|
|
155
|
+
clearAllCachedTokens() {
|
|
156
|
+
this.db
|
|
157
|
+
.prepare(`UPDATE installations SET token_enc = NULL, token_expires_at = NULL, token_kid = NULL`)
|
|
158
|
+
.run();
|
|
159
|
+
}
|
|
160
|
+
deleteInstallation(installationId) {
|
|
161
|
+
this.db.prepare(`DELETE FROM installations WHERE installation_id = ?`).run(installationId);
|
|
162
|
+
}
|
|
163
|
+
markInstallationPending(installationId) {
|
|
164
|
+
this.db.prepare(`UPDATE installations SET pending = 1 WHERE installation_id = ?`).run(installationId);
|
|
165
|
+
}
|
|
166
|
+
clearInstallationPending(installationId) {
|
|
167
|
+
this.db.prepare(`UPDATE installations SET pending = 0 WHERE installation_id = ?`).run(installationId);
|
|
168
|
+
}
|
|
169
|
+
isKnownPending(installationId) {
|
|
170
|
+
const row = this.db
|
|
171
|
+
.prepare(`SELECT pending FROM installations WHERE installation_id = ?`)
|
|
172
|
+
.get(installationId);
|
|
173
|
+
if (!row)
|
|
174
|
+
return false;
|
|
175
|
+
return toPendingFlag(row.pending);
|
|
176
|
+
}
|
|
177
|
+
listWebhookSecrets() {
|
|
178
|
+
const rows = this.db
|
|
179
|
+
.prepare(`SELECT installation_id, webhook_secret_enc FROM installations WHERE webhook_secret_enc IS NOT NULL`)
|
|
180
|
+
.all();
|
|
181
|
+
return rows.map((row) => ({
|
|
182
|
+
installationId: row.installation_id,
|
|
183
|
+
secret: decryptSecret(row.webhook_secret_enc, this.key),
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
hasSeenWebhookId(webhookId) {
|
|
187
|
+
this.pruneSeenIds();
|
|
188
|
+
return (this.db.prepare(`SELECT 1 AS one FROM seen_webhook_ids WHERE webhook_id = ?`).get(webhookId) !==
|
|
189
|
+
undefined);
|
|
190
|
+
}
|
|
191
|
+
markWebhookSeen(webhookId) {
|
|
192
|
+
this.db
|
|
193
|
+
.prepare(`INSERT OR IGNORE INTO seen_webhook_ids (webhook_id, seen_at) VALUES (?, ?)`)
|
|
194
|
+
.run(webhookId, Date.now());
|
|
195
|
+
}
|
|
196
|
+
claimWebhookId(webhookId) {
|
|
197
|
+
this.pruneSeenIds();
|
|
198
|
+
const result = this.db
|
|
199
|
+
.prepare(`INSERT OR IGNORE INTO seen_webhook_ids (webhook_id, seen_at) VALUES (?, ?)`)
|
|
200
|
+
.run(webhookId, Date.now());
|
|
201
|
+
return Number(result.changes) === 1;
|
|
202
|
+
}
|
|
203
|
+
releaseWebhookId(webhookId) {
|
|
204
|
+
this.db.prepare(`DELETE FROM seen_webhook_ids WHERE webhook_id = ?`).run(webhookId);
|
|
205
|
+
}
|
|
206
|
+
pruneSeenIds() {
|
|
207
|
+
this.db
|
|
208
|
+
.prepare(`DELETE FROM seen_webhook_ids WHERE seen_at < ?`)
|
|
209
|
+
.run(Date.now() - SEEN_TTL_SECONDS * 1000);
|
|
210
|
+
}
|
|
211
|
+
close() {
|
|
212
|
+
this.db.close();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function rowToRecord(row, key) {
|
|
216
|
+
return {
|
|
217
|
+
installationId: column(row.installation_id),
|
|
218
|
+
installationPid: column(row.installation_pid),
|
|
219
|
+
vendorId: column(row.vendor_id),
|
|
220
|
+
storePid: row.store_pid ?? null,
|
|
221
|
+
storeName: column(row.store_name),
|
|
222
|
+
apiBase: column(row.api_base),
|
|
223
|
+
token: row.token_enc === null ? null : decryptSecret(column(row.token_enc), key),
|
|
224
|
+
tokenExpiresAt: row.token_expires_at ?? null,
|
|
225
|
+
tokenKid: row.token_kid ?? null,
|
|
226
|
+
pending: toPendingFlag(row.pending),
|
|
227
|
+
scopes: JSON.parse(column(row.scopes_json)),
|
|
228
|
+
settings: JSON.parse(decryptSecret(column(row.settings_json), key)),
|
|
229
|
+
webhookSecret: row.webhook_secret_enc === null ? null : decryptSecret(column(row.webhook_secret_enc), key),
|
|
230
|
+
proxySecret: row.proxy_secret_enc === null || row.proxy_secret_enc === undefined
|
|
231
|
+
? null
|
|
232
|
+
: decryptSecret(column(row.proxy_secret_enc), key),
|
|
233
|
+
embedSecret: row.embed_secret_enc === null || row.embed_secret_enc === undefined
|
|
234
|
+
? null
|
|
235
|
+
: decryptSecret(column(row.embed_secret_enc), key),
|
|
236
|
+
appId: row.app_id ?? null,
|
|
237
|
+
webhookUrl: row.webhook_url ?? null,
|
|
238
|
+
webhookTopics: JSON.parse(column(row.webhook_topics_json)),
|
|
239
|
+
installedAt: column(row.installed_at),
|
|
240
|
+
updatedAt: column(row.updated_at),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function column(value) {
|
|
244
|
+
if (typeof value !== "string")
|
|
245
|
+
throw new Error("Unexpected non-string column in installations table.");
|
|
246
|
+
return value;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Read the persisted pending flag across drivers: SQLite stores
|
|
250
|
+
* `INTEGER 0/1`, Postgres `BOOLEAN`. Anything unrecognised (including
|
|
251
|
+
* NULL on a pre-migration row, which cannot happen — the column is NOT
|
|
252
|
+
* NULL with a default — but belt-and-braces) reads as not pending.
|
|
253
|
+
*/
|
|
254
|
+
function toPendingFlag(value) {
|
|
255
|
+
return value === true || value === 1 || value === "1" || value === "t" || value === "true";
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Current schema revision, stored as exactly one row in `schema_version`
|
|
259
|
+
* (Postgres) and enforced by column-presence migration (SQLite).
|
|
260
|
+
* v2 adds the persisted 409-pending mark (`pending`, NOT NULL DEFAULT
|
|
261
|
+
* false on both drivers); v3 the proxy secret; v4 the embed secret and
|
|
262
|
+
* app id that verify dashboard session tokens (all nullable).
|
|
263
|
+
*/
|
|
264
|
+
export const INSTALLATION_SCHEMA_VERSION = 4;
|
|
265
|
+
/**
|
|
266
|
+
* Build one shared `pg` Pool for a process (the SDK store and the app's
|
|
267
|
+
* own store both run on it — total connections per app stay small: one
|
|
268
|
+
* pool of N per process instead of one pool per store). Attaches the same
|
|
269
|
+
* idle-client error guard the store constructors use, so a dropped idle
|
|
270
|
+
* connection never surfaces as an uncaught exception.
|
|
271
|
+
*/
|
|
272
|
+
export function createPostgresPool(connectionString, max = 2) {
|
|
273
|
+
const pool = new Pool({ connectionString, max });
|
|
274
|
+
pool.on("error", () => {
|
|
275
|
+
// Idle-client errors have no request to fail: without this listener
|
|
276
|
+
// they throw as uncaught exceptions. Query errors still reject their
|
|
277
|
+
// own promises. (Deliberately no logging here: the store owns no
|
|
278
|
+
// logger, and logging pool internals would risk leaking the DSN.)
|
|
279
|
+
});
|
|
280
|
+
return pool;
|
|
281
|
+
}
|
|
282
|
+
const SCHEMA_ADVISORY_LOCK = "queek-app-sdk:installations-schema-v1";
|
|
283
|
+
/**
|
|
284
|
+
* Production `InstallationStore`: Postgres through `pg` (`Pool`, max 2).
|
|
285
|
+
*
|
|
286
|
+
* Schema safety when two containers boot together: the first statement of
|
|
287
|
+
* `ensureSchema()` takes a Postgres advisory lock, then creates the tables
|
|
288
|
+
* plus exactly one `schema_version` row, then releases the lock — so
|
|
289
|
+
* concurrent boots serialize instead of racing DDL. Every store method
|
|
290
|
+
* awaits the same cached init promise, so no query runs before the schema
|
|
291
|
+
* exists. The AES-256-GCM envelope is byte-identical to the SQLite store
|
|
292
|
+
* (same `encryptSecret`/`decryptSecret`, same `APP_ENCRYPTION_KEY`).
|
|
293
|
+
*/
|
|
294
|
+
export class PostgresInstallationStore {
|
|
295
|
+
pool;
|
|
296
|
+
ownsPool;
|
|
297
|
+
key;
|
|
298
|
+
ready;
|
|
299
|
+
constructor(options) {
|
|
300
|
+
this.key = parseStoreKey(options.storeKey);
|
|
301
|
+
if (options.pool) {
|
|
302
|
+
this.pool = options.pool;
|
|
303
|
+
this.ownsPool = false;
|
|
304
|
+
}
|
|
305
|
+
else if (options.connectionString) {
|
|
306
|
+
this.pool = createPostgresPool(options.connectionString, options.poolMax ?? 2);
|
|
307
|
+
this.ownsPool = true;
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
throw new Error("PostgresInstallationStore needs a connectionString or a shared pool.");
|
|
311
|
+
}
|
|
312
|
+
this.ready = this.ensureSchema();
|
|
313
|
+
// Methods awaiting `ready` surface a connection failure themselves;
|
|
314
|
+
// this preempts an unhandled rejection when Postgres is unreachable at
|
|
315
|
+
// construction (the first call still throws the real error).
|
|
316
|
+
this.ready.catch(() => undefined);
|
|
317
|
+
}
|
|
318
|
+
async ensureSchema() {
|
|
319
|
+
const client = await this.pool.connect();
|
|
320
|
+
try {
|
|
321
|
+
await client.query(`SELECT pg_advisory_lock(hashtext($1))`, [SCHEMA_ADVISORY_LOCK]);
|
|
322
|
+
try {
|
|
323
|
+
await client.query(`
|
|
324
|
+
CREATE TABLE IF NOT EXISTS schema_version (
|
|
325
|
+
version INTEGER PRIMARY KEY,
|
|
326
|
+
applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
327
|
+
);
|
|
328
|
+
CREATE TABLE IF NOT EXISTS installations (
|
|
329
|
+
installation_id TEXT PRIMARY KEY,
|
|
330
|
+
installation_pid TEXT NOT NULL,
|
|
331
|
+
vendor_id TEXT NOT NULL,
|
|
332
|
+
store_pid TEXT,
|
|
333
|
+
store_name TEXT NOT NULL,
|
|
334
|
+
api_base TEXT NOT NULL,
|
|
335
|
+
token_enc TEXT,
|
|
336
|
+
token_expires_at TIMESTAMPTZ,
|
|
337
|
+
token_kid TEXT,
|
|
338
|
+
pending BOOLEAN NOT NULL DEFAULT FALSE,
|
|
339
|
+
scopes_json TEXT NOT NULL,
|
|
340
|
+
settings_json TEXT NOT NULL,
|
|
341
|
+
webhook_secret_enc TEXT,
|
|
342
|
+
proxy_secret_enc TEXT,
|
|
343
|
+
embed_secret_enc TEXT,
|
|
344
|
+
app_id TEXT,
|
|
345
|
+
webhook_url TEXT,
|
|
346
|
+
webhook_topics_json TEXT NOT NULL,
|
|
347
|
+
installed_at TIMESTAMPTZ NOT NULL,
|
|
348
|
+
updated_at TIMESTAMPTZ NOT NULL
|
|
349
|
+
);
|
|
350
|
+
CREATE TABLE IF NOT EXISTS seen_webhook_ids (
|
|
351
|
+
webhook_id TEXT PRIMARY KEY,
|
|
352
|
+
seen_at BIGINT NOT NULL
|
|
353
|
+
);
|
|
354
|
+
-- Schema v2 (B2 review r2): the persisted 409-pending mark.
|
|
355
|
+
-- Fresh tables already carry the column; this migrates v1
|
|
356
|
+
-- databases in place (existing rows backfill to FALSE).
|
|
357
|
+
ALTER TABLE installations
|
|
358
|
+
ADD COLUMN IF NOT EXISTS pending BOOLEAN NOT NULL DEFAULT FALSE;
|
|
359
|
+
-- Schema v3 (FA1): the installation proxy secret for slot-claim
|
|
360
|
+
-- signing. Existing rows backfill to NULL (unknown until the
|
|
361
|
+
-- next install/resync handoff).
|
|
362
|
+
ALTER TABLE installations
|
|
363
|
+
ADD COLUMN IF NOT EXISTS proxy_secret_enc TEXT;
|
|
364
|
+
-- Schema v4 (S4): the embed secret + app id that verify
|
|
365
|
+
-- dashboard session tokens (NULL until the next handoff).
|
|
366
|
+
ALTER TABLE installations
|
|
367
|
+
ADD COLUMN IF NOT EXISTS embed_secret_enc TEXT;
|
|
368
|
+
ALTER TABLE installations
|
|
369
|
+
ADD COLUMN IF NOT EXISTS app_id TEXT;
|
|
370
|
+
-- The guard row always converges to exactly one row at the
|
|
371
|
+
-- current version: v1 databases gain the v2 row and lose the v1
|
|
372
|
+
-- row; fresh databases insert it directly.
|
|
373
|
+
INSERT INTO schema_version (version) VALUES (${INSTALLATION_SCHEMA_VERSION})
|
|
374
|
+
ON CONFLICT (version) DO NOTHING;
|
|
375
|
+
DELETE FROM schema_version WHERE version < ${INSTALLATION_SCHEMA_VERSION};
|
|
376
|
+
`);
|
|
377
|
+
}
|
|
378
|
+
finally {
|
|
379
|
+
await client.query(`SELECT pg_advisory_unlock(hashtext($1))`, [SCHEMA_ADVISORY_LOCK]);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
finally {
|
|
383
|
+
client.release();
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
async saveInstallation(record) {
|
|
387
|
+
await this.ready;
|
|
388
|
+
const now = new Date().toISOString();
|
|
389
|
+
await this.pool.query(`INSERT INTO installations (
|
|
390
|
+
installation_id, installation_pid, vendor_id, store_pid, store_name,
|
|
391
|
+
api_base, token_enc, token_expires_at, token_kid, pending,
|
|
392
|
+
scopes_json, settings_json,
|
|
393
|
+
webhook_secret_enc, proxy_secret_enc, embed_secret_enc, app_id, webhook_url, webhook_topics_json,
|
|
394
|
+
installed_at, updated_at
|
|
395
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
|
|
396
|
+
ON CONFLICT (installation_id) DO UPDATE SET
|
|
397
|
+
installation_pid = excluded.installation_pid,
|
|
398
|
+
vendor_id = excluded.vendor_id,
|
|
399
|
+
store_pid = excluded.store_pid,
|
|
400
|
+
store_name = excluded.store_name,
|
|
401
|
+
api_base = excluded.api_base,
|
|
402
|
+
token_enc = excluded.token_enc,
|
|
403
|
+
token_expires_at = excluded.token_expires_at,
|
|
404
|
+
token_kid = excluded.token_kid,
|
|
405
|
+
pending = excluded.pending,
|
|
406
|
+
scopes_json = excluded.scopes_json,
|
|
407
|
+
settings_json = excluded.settings_json,
|
|
408
|
+
webhook_secret_enc = excluded.webhook_secret_enc,
|
|
409
|
+
proxy_secret_enc = excluded.proxy_secret_enc,
|
|
410
|
+
embed_secret_enc = excluded.embed_secret_enc,
|
|
411
|
+
app_id = excluded.app_id,
|
|
412
|
+
webhook_url = excluded.webhook_url,
|
|
413
|
+
webhook_topics_json = excluded.webhook_topics_json,
|
|
414
|
+
updated_at = excluded.updated_at`, [
|
|
415
|
+
record.installationId,
|
|
416
|
+
record.installationPid,
|
|
417
|
+
record.vendorId,
|
|
418
|
+
record.storePid,
|
|
419
|
+
record.storeName,
|
|
420
|
+
record.apiBase,
|
|
421
|
+
record.token === null ? null : encryptSecret(record.token, this.key),
|
|
422
|
+
record.tokenExpiresAt,
|
|
423
|
+
record.tokenKid,
|
|
424
|
+
record.pending,
|
|
425
|
+
JSON.stringify(record.scopes),
|
|
426
|
+
encryptSecret(JSON.stringify(record.settings), this.key),
|
|
427
|
+
record.webhookSecret === null ? null : encryptSecret(record.webhookSecret, this.key),
|
|
428
|
+
record.proxySecret === null || record.proxySecret === undefined
|
|
429
|
+
? null
|
|
430
|
+
: encryptSecret(record.proxySecret, this.key),
|
|
431
|
+
record.embedSecret === null || record.embedSecret === undefined
|
|
432
|
+
? null
|
|
433
|
+
: encryptSecret(record.embedSecret, this.key),
|
|
434
|
+
record.appId ?? null,
|
|
435
|
+
record.webhookUrl,
|
|
436
|
+
JSON.stringify(record.webhookTopics),
|
|
437
|
+
record.installedAt,
|
|
438
|
+
now,
|
|
439
|
+
]);
|
|
440
|
+
}
|
|
441
|
+
async getInstallation(installationId) {
|
|
442
|
+
await this.ready;
|
|
443
|
+
const result = await this.pool.query(`SELECT * FROM installations WHERE installation_id = $1`, [
|
|
444
|
+
installationId,
|
|
445
|
+
]);
|
|
446
|
+
const row = result.rows[0];
|
|
447
|
+
if (!row)
|
|
448
|
+
return null;
|
|
449
|
+
return pgRowToRecord(row, this.key);
|
|
450
|
+
}
|
|
451
|
+
async listInstallations() {
|
|
452
|
+
await this.ready;
|
|
453
|
+
const result = await this.pool.query(`SELECT * FROM installations ORDER BY installation_id`);
|
|
454
|
+
return result.rows.map((row) => pgRowToRecord(row, this.key));
|
|
455
|
+
}
|
|
456
|
+
async clearCachedToken(installationId) {
|
|
457
|
+
await this.ready;
|
|
458
|
+
await this.pool.query(`UPDATE installations SET token_enc = NULL, token_expires_at = NULL, token_kid = NULL
|
|
459
|
+
WHERE installation_id = $1`, [installationId]);
|
|
460
|
+
}
|
|
461
|
+
async clearAllCachedTokens() {
|
|
462
|
+
await this.ready;
|
|
463
|
+
await this.pool.query(`UPDATE installations SET token_enc = NULL, token_expires_at = NULL, token_kid = NULL`);
|
|
464
|
+
}
|
|
465
|
+
async deleteInstallation(installationId) {
|
|
466
|
+
await this.ready;
|
|
467
|
+
await this.pool.query(`DELETE FROM installations WHERE installation_id = $1`, [installationId]);
|
|
468
|
+
}
|
|
469
|
+
async markInstallationPending(installationId) {
|
|
470
|
+
await this.ready;
|
|
471
|
+
await this.pool.query(`UPDATE installations SET pending = TRUE WHERE installation_id = $1`, [
|
|
472
|
+
installationId,
|
|
473
|
+
]);
|
|
474
|
+
}
|
|
475
|
+
async clearInstallationPending(installationId) {
|
|
476
|
+
await this.ready;
|
|
477
|
+
await this.pool.query(`UPDATE installations SET pending = FALSE WHERE installation_id = $1`, [
|
|
478
|
+
installationId,
|
|
479
|
+
]);
|
|
480
|
+
}
|
|
481
|
+
async isKnownPending(installationId) {
|
|
482
|
+
await this.ready;
|
|
483
|
+
const result = await this.pool.query(`SELECT pending FROM installations WHERE installation_id = $1`, [
|
|
484
|
+
installationId,
|
|
485
|
+
]);
|
|
486
|
+
const row = result.rows[0];
|
|
487
|
+
if (!row)
|
|
488
|
+
return false;
|
|
489
|
+
return toPendingFlag(row.pending);
|
|
490
|
+
}
|
|
491
|
+
async listWebhookSecrets() {
|
|
492
|
+
await this.ready;
|
|
493
|
+
const result = await this.pool.query(`SELECT installation_id, webhook_secret_enc FROM installations WHERE webhook_secret_enc IS NOT NULL`);
|
|
494
|
+
return result.rows.map((row) => ({
|
|
495
|
+
installationId: row.installation_id,
|
|
496
|
+
secret: decryptSecret(row.webhook_secret_enc, this.key),
|
|
497
|
+
}));
|
|
498
|
+
}
|
|
499
|
+
async hasSeenWebhookId(webhookId) {
|
|
500
|
+
await this.ready;
|
|
501
|
+
await this.pruneSeenIds();
|
|
502
|
+
const result = await this.pool.query(`SELECT 1 AS one FROM seen_webhook_ids WHERE webhook_id = $1`, [
|
|
503
|
+
webhookId,
|
|
504
|
+
]);
|
|
505
|
+
return result.rowCount !== 0;
|
|
506
|
+
}
|
|
507
|
+
async markWebhookSeen(webhookId) {
|
|
508
|
+
await this.ready;
|
|
509
|
+
await this.pool.query(`INSERT INTO seen_webhook_ids (webhook_id, seen_at) VALUES ($1, $2) ON CONFLICT DO NOTHING`, [webhookId, Date.now()]);
|
|
510
|
+
}
|
|
511
|
+
async claimWebhookId(webhookId) {
|
|
512
|
+
await this.ready;
|
|
513
|
+
await this.pruneSeenIds();
|
|
514
|
+
const result = await this.pool.query(`INSERT INTO seen_webhook_ids (webhook_id, seen_at) VALUES ($1, $2)
|
|
515
|
+
ON CONFLICT (webhook_id) DO NOTHING RETURNING webhook_id`, [webhookId, Date.now()]);
|
|
516
|
+
return (result.rowCount ?? 0) === 1;
|
|
517
|
+
}
|
|
518
|
+
async releaseWebhookId(webhookId) {
|
|
519
|
+
await this.ready;
|
|
520
|
+
await this.pool.query(`DELETE FROM seen_webhook_ids WHERE webhook_id = $1`, [webhookId]);
|
|
521
|
+
}
|
|
522
|
+
async pruneSeenIds() {
|
|
523
|
+
await this.pool.query(`DELETE FROM seen_webhook_ids WHERE seen_at < $1`, [
|
|
524
|
+
Date.now() - SEEN_TTL_SECONDS * 1000,
|
|
525
|
+
]);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Drain the pool (tests and graceful shutdown). A no-op when the store
|
|
529
|
+
* runs on a shared pool it does not own — the pool owner ends it.
|
|
530
|
+
*/
|
|
531
|
+
async close() {
|
|
532
|
+
if (this.ownsPool)
|
|
533
|
+
await this.pool.end();
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
function pgText(value, name) {
|
|
537
|
+
if (typeof value !== "string")
|
|
538
|
+
throw new Error(`Unexpected non-string column ${name}.`);
|
|
539
|
+
return value;
|
|
540
|
+
}
|
|
541
|
+
function pgRowToRecord(row, key) {
|
|
542
|
+
const tokenEnc = row.token_enc;
|
|
543
|
+
const webhookSecretEnc = row.webhook_secret_enc;
|
|
544
|
+
const expires = row.token_expires_at;
|
|
545
|
+
return {
|
|
546
|
+
installationId: pgText(row.installation_id, "installation_id"),
|
|
547
|
+
installationPid: pgText(row.installation_pid, "installation_pid"),
|
|
548
|
+
vendorId: pgText(row.vendor_id, "vendor_id"),
|
|
549
|
+
storePid: row.store_pid ?? null,
|
|
550
|
+
storeName: pgText(row.store_name, "store_name"),
|
|
551
|
+
apiBase: pgText(row.api_base, "api_base"),
|
|
552
|
+
token: tokenEnc === null ? null : decryptSecret(tokenEnc, key),
|
|
553
|
+
tokenExpiresAt: expires === null
|
|
554
|
+
? null
|
|
555
|
+
: expires instanceof Date
|
|
556
|
+
? expires.toISOString()
|
|
557
|
+
: new Date(expires).toISOString(),
|
|
558
|
+
tokenKid: row.token_kid ?? null,
|
|
559
|
+
pending: toPendingFlag(row.pending),
|
|
560
|
+
scopes: JSON.parse(pgText(row.scopes_json, "scopes_json")),
|
|
561
|
+
settings: JSON.parse(decryptSecret(pgText(row.settings_json, "settings_json"), key)),
|
|
562
|
+
webhookSecret: webhookSecretEnc === null ? null : decryptSecret(webhookSecretEnc, key),
|
|
563
|
+
proxySecret: (() => {
|
|
564
|
+
const enc = row.proxy_secret_enc;
|
|
565
|
+
return enc === null || enc === undefined ? null : decryptSecret(enc, key);
|
|
566
|
+
})(),
|
|
567
|
+
embedSecret: (() => {
|
|
568
|
+
const enc = row.embed_secret_enc;
|
|
569
|
+
return enc === null || enc === undefined ? null : decryptSecret(enc, key);
|
|
570
|
+
})(),
|
|
571
|
+
appId: row.app_id ?? null,
|
|
572
|
+
webhookUrl: row.webhook_url ?? null,
|
|
573
|
+
webhookTopics: JSON.parse(pgText(row.webhook_topics_json, "webhook_topics_json")),
|
|
574
|
+
installedAt: toIso(row.installed_at),
|
|
575
|
+
updatedAt: toIso(row.updated_at),
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
function toIso(value) {
|
|
579
|
+
if (value instanceof Date)
|
|
580
|
+
return value.toISOString();
|
|
581
|
+
return new Date(pgText(value, "timestamp")).toISOString();
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Pick the installation store: `DATABASE_URL` set (or a shared `pool`
|
|
585
|
+
* given) → Postgres; otherwise SQLite — which production REFUSES (fail
|
|
586
|
+
* fast with a clear message: a live app must never run on a
|
|
587
|
+
* container-local database).
|
|
588
|
+
*/
|
|
589
|
+
export function createInstallationStore(options) {
|
|
590
|
+
if (options.pool) {
|
|
591
|
+
return new PostgresInstallationStore({ storeKey: options.storeKey, pool: options.pool });
|
|
592
|
+
}
|
|
593
|
+
const databaseUrl = (options.databaseUrl ?? process.env.DATABASE_URL ?? "").trim();
|
|
594
|
+
if (databaseUrl !== "") {
|
|
595
|
+
return new PostgresInstallationStore({
|
|
596
|
+
connectionString: databaseUrl,
|
|
597
|
+
storeKey: options.storeKey,
|
|
598
|
+
poolMax: options.poolMax,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
if ((process.env.NODE_ENV ?? "") === "production") {
|
|
602
|
+
throw new Error("Refusing to start on SQLite in production: set DATABASE_URL to the app's Postgres database " +
|
|
603
|
+
"(one database per app; SQLite is local/dev/test only).");
|
|
604
|
+
}
|
|
605
|
+
return new SqliteInstallationStore({
|
|
606
|
+
path: options.sqlitePath ?? process.env.QUEEK_DB_PATH ?? "./data/installations.db",
|
|
607
|
+
storeKey: options.storeKey,
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|