@oneaddress/setup 1.1.4 → 1.1.5
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/dist/index.js +125 -71
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -835,7 +835,7 @@ var _R = ["\u2588\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588"
|
|
|
835
835
|
var _S = [" \u2588\u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 ", "\u2588\u2588 ", " \u2588\u2588\u2588\u2588\u2588 ", " \u2588\u2588", " \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588\u2588 "];
|
|
836
836
|
var ONE_ROWS = Array.from({ length: 7 }, (_2, i) => [_O[i], _N[i], _E[i]].join(" "));
|
|
837
837
|
var ADDR_ROWS = Array.from({ length: 7 }, (_2, i) => [_A[i], _D2[i], _D2[i], _R[i], _E[i], _S[i], _S[i]].join(" "));
|
|
838
|
-
var WIZARD_VERSION = true ? "1.1.
|
|
838
|
+
var WIZARD_VERSION = true ? "1.1.5" : "?";
|
|
839
839
|
function printHeader() {
|
|
840
840
|
const BAR_LEN = 85;
|
|
841
841
|
const TOP_BAR = fn("\u250C") + dm("\u2500".repeat(BAR_LEN)) + fn("\u2510");
|
|
@@ -927,27 +927,81 @@ dist/
|
|
|
927
927
|
},
|
|
928
928
|
"devDependencies": {
|
|
929
929
|
"@types/express": "^4.17.0",
|
|
930
|
-
"@types/node": "^
|
|
930
|
+
"@types/node": "^22.5.0",
|
|
931
931
|
"tsup": "^8.0.0",
|
|
932
932
|
"tsx": "^4.0.0",
|
|
933
933
|
"typescript": "^5.0.0"
|
|
934
934
|
},
|
|
935
|
-
"engines": { "node": ">=
|
|
935
|
+
"engines": { "node": ">=22.5" }
|
|
936
936
|
}
|
|
937
|
+
`
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
name: "src/db.ts",
|
|
941
|
+
content: `/**
|
|
942
|
+
* SQLite database \u2014 initialised automatically on first run.
|
|
943
|
+
*
|
|
944
|
+
* Uses the Node.js built-in \`node:sqlite\` module (available in Node 22.5+).
|
|
945
|
+
* No extra packages or native compilation required.
|
|
946
|
+
*
|
|
947
|
+
* Two tables:
|
|
948
|
+
* addresses \u2014 one row per customer, holds their latest address
|
|
949
|
+
* address_history \u2014 append-only audit trail of every change
|
|
950
|
+
*
|
|
951
|
+
* The database file lives at DB_PATH (default: data.db in the project root).
|
|
952
|
+
* Change the location with the DB_PATH environment variable.
|
|
953
|
+
*/
|
|
954
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
955
|
+
import { join } from 'node:path';
|
|
956
|
+
|
|
957
|
+
const DB_PATH = process.env.DB_PATH ?? join(process.cwd(), 'data.db');
|
|
958
|
+
|
|
959
|
+
const db = new DatabaseSync(DB_PATH);
|
|
960
|
+
|
|
961
|
+
// WAL mode \u2014 better performance for concurrent reads
|
|
962
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
963
|
+
|
|
964
|
+
db.exec(\`
|
|
965
|
+
CREATE TABLE IF NOT EXISTS addresses (
|
|
966
|
+
email TEXT PRIMARY KEY,
|
|
967
|
+
name TEXT NOT NULL DEFAULT '',
|
|
968
|
+
address TEXT NOT NULL DEFAULT '{}',
|
|
969
|
+
dispatch_id TEXT,
|
|
970
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
971
|
+
);
|
|
972
|
+
|
|
973
|
+
CREATE TABLE IF NOT EXISTS address_history (
|
|
974
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
975
|
+
email TEXT NOT NULL,
|
|
976
|
+
name TEXT NOT NULL DEFAULT '',
|
|
977
|
+
address TEXT NOT NULL DEFAULT '{}',
|
|
978
|
+
dispatch_id TEXT,
|
|
979
|
+
recorded_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
980
|
+
);
|
|
981
|
+
\`);
|
|
982
|
+
|
|
983
|
+
console.log(\`[db] SQLite database ready \u2192 \${DB_PATH}\`);
|
|
984
|
+
|
|
985
|
+
export default db;
|
|
937
986
|
`
|
|
938
987
|
},
|
|
939
988
|
{
|
|
940
989
|
name: "src/store.ts",
|
|
941
990
|
content: `/**
|
|
942
991
|
* \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
943
|
-
* \u2551 src/store.ts \u2014
|
|
992
|
+
* \u2551 src/store.ts \u2014 DATABASE INTEGRATION \u2551
|
|
993
|
+
* \u2551 \u2551
|
|
994
|
+
* \u2551 Uses Node.js built-in SQLite (node:sqlite) \u2014 no install \u2551
|
|
995
|
+
* \u2551 or native compilation needed. Requires Node.js 22.5+. \u2551
|
|
996
|
+
* \u2551 The database file is created automatically as data.db \u2551
|
|
944
997
|
* \u2551 \u2551
|
|
945
|
-
* \u2551
|
|
946
|
-
* \u2551
|
|
947
|
-
* \u2551 server.ts calls
|
|
998
|
+
* \u2551 To use a different database (Postgres, MySQL, etc.): \u2551
|
|
999
|
+
* \u2551 Replace the db calls in saveAddress and verifyAddress below. \u2551
|
|
1000
|
+
* \u2551 server.ts calls these after verifying and decrypting each event \u2551
|
|
948
1001
|
* \u2551 \u2014 the protocol layer is handled for you, never touch it. \u2551
|
|
949
1002
|
* \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
950
1003
|
*/
|
|
1004
|
+
import db from './db.js';
|
|
951
1005
|
|
|
952
1006
|
export type Address = Record<string, unknown>;
|
|
953
1007
|
|
|
@@ -961,47 +1015,34 @@ export type VerifyResult = 'match' | 'mismatch' | 'not_found';
|
|
|
961
1015
|
/**
|
|
962
1016
|
* Called when a consumer updates their address (address.updated event).
|
|
963
1017
|
*
|
|
964
|
-
*
|
|
965
|
-
*
|
|
966
|
-
*
|
|
967
|
-
* Persist this to your database (update the customer's address record,
|
|
968
|
-
* publish to a queue, call an internal API \u2014 whatever your system needs).
|
|
1018
|
+
* Upserts the address into the \`addresses\` table and appends a row
|
|
1019
|
+
* to \`address_history\` for the audit trail.
|
|
969
1020
|
*/
|
|
970
1021
|
export async function saveAddress(customer: Customer, address: Address): Promise<void> {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
// await fetch('https://your-crm.internal/addresses', {
|
|
989
|
-
// method: 'PUT',
|
|
990
|
-
// body: JSON.stringify({ email: customer.email, address }),
|
|
991
|
-
// });
|
|
992
|
-
//
|
|
993
|
-
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
994
|
-
|
|
995
|
-
console.log('[store] saveAddress \u2014 implement this function to persist the address');
|
|
996
|
-
console.log('[store] customer:', customer.email);
|
|
997
|
-
console.log('[store] address: ', JSON.stringify(address));
|
|
1022
|
+
const addressJson = JSON.stringify(address);
|
|
1023
|
+
|
|
1024
|
+
db.prepare(\`
|
|
1025
|
+
INSERT INTO addresses (email, name, address, updated_at)
|
|
1026
|
+
VALUES ($email, $name, $address, datetime('now'))
|
|
1027
|
+
ON CONFLICT(email) DO UPDATE SET
|
|
1028
|
+
name = excluded.name,
|
|
1029
|
+
address = excluded.address,
|
|
1030
|
+
updated_at = excluded.updated_at
|
|
1031
|
+
\`).run({ email: customer.email, name: customer.name, address: addressJson });
|
|
1032
|
+
|
|
1033
|
+
db.prepare(\`
|
|
1034
|
+
INSERT INTO address_history (email, name, address, recorded_at)
|
|
1035
|
+
VALUES ($email, $name, $address, datetime('now'))
|
|
1036
|
+
\`).run({ email: customer.email, name: customer.name, address: addressJson });
|
|
1037
|
+
|
|
1038
|
+
console.log(\`[store] Saved address for \${customer.email}:\`, address);
|
|
998
1039
|
}
|
|
999
1040
|
|
|
1000
1041
|
/**
|
|
1001
1042
|
* Called during an address verification check (address.verify event).
|
|
1002
1043
|
*
|
|
1003
|
-
*
|
|
1004
|
-
*
|
|
1044
|
+
* Looks up the customer by email and compares the incoming (decrypted)
|
|
1045
|
+
* address against what is stored in the database.
|
|
1005
1046
|
*
|
|
1006
1047
|
* Return values:
|
|
1007
1048
|
* 'match' \u2014 the address matches your records exactly
|
|
@@ -1009,19 +1050,20 @@ export async function saveAddress(customer: Customer, address: Address): Promise
|
|
|
1009
1050
|
* 'not_found' \u2014 you have no record for this customer at all
|
|
1010
1051
|
*/
|
|
1011
1052
|
export async function verifyAddress(customer: Customer, address: Address): Promise<VerifyResult> {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1053
|
+
const row = db.prepare(\`SELECT address FROM addresses WHERE email = ?\`)
|
|
1054
|
+
.get(customer.email) as { address: string } | undefined;
|
|
1055
|
+
|
|
1056
|
+
if (!row) {
|
|
1057
|
+
console.log(\`[store] verifyAddress \u2192 not_found (\${customer.email} has no record)\`);
|
|
1058
|
+
return 'not_found';
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
const stored = JSON.parse(row.address) as Address;
|
|
1062
|
+
const isMatch = Object.entries(address).every(([k, v]) => stored[k] === v);
|
|
1063
|
+
const result = isMatch ? 'match' : 'mismatch';
|
|
1064
|
+
|
|
1065
|
+
console.log(\`[store] verifyAddress \u2192 \${result} for \${customer.email}\`);
|
|
1066
|
+
return result;
|
|
1025
1067
|
}
|
|
1026
1068
|
`
|
|
1027
1069
|
},
|
|
@@ -1211,26 +1253,39 @@ npm run dev # hot-reload via tsx
|
|
|
1211
1253
|
Webhook endpoint: \`POST http://localhost:3001/webhook\`
|
|
1212
1254
|
Health check: \`GET http://localhost:3001/health\`
|
|
1213
1255
|
|
|
1256
|
+
A SQLite database (\`data.db\`) is created automatically the first time the server starts.
|
|
1257
|
+
To view it, open \`data.db\` with any SQLite client (e.g. [DB Browser for SQLite](https://sqlitebrowser.org/)).
|
|
1258
|
+
|
|
1214
1259
|
## Architecture
|
|
1215
1260
|
|
|
1216
1261
|
| File | Role |
|
|
1217
1262
|
|------|------|
|
|
1218
1263
|
| \`src/server.ts\` | **Protocol layer \u2014 do not edit.** HMAC verification, timestamp replay protection, ECDH decryption, deduplication. |
|
|
1219
|
-
| \`src/
|
|
1220
|
-
|
|
1221
|
-
## Implementing src/store.ts
|
|
1264
|
+
| \`src/db.ts\` | **SQLite setup.** Opens (or creates) \`data.db\` and defines the schema. Change \`DB_PATH\` env var to move the file. |
|
|
1265
|
+
| \`src/store.ts\` | **Your integration.** \`saveAddress\` and \`verifyAddress\` \u2014 wired to SQLite out of the box. Swap for Postgres/MySQL/etc. when ready. |
|
|
1222
1266
|
|
|
1223
|
-
|
|
1267
|
+
## Database tables
|
|
1224
1268
|
|
|
1225
|
-
\`\`\`
|
|
1226
|
-
|
|
1227
|
-
|
|
1269
|
+
\`\`\`sql
|
|
1270
|
+
-- Current address per customer (upserted on every address.updated event)
|
|
1271
|
+
addresses(email, name, address JSON, updated_at)
|
|
1228
1272
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1273
|
+
-- Full history of every address change (append-only audit trail)
|
|
1274
|
+
address_history(id, email, name, address JSON, recorded_at)
|
|
1231
1275
|
\`\`\`
|
|
1232
1276
|
|
|
1233
|
-
|
|
1277
|
+
## Swapping to a production database
|
|
1278
|
+
|
|
1279
|
+
Open \`src/store.ts\` and replace the \`db\` calls with your ORM/driver of choice:
|
|
1280
|
+
|
|
1281
|
+
\`\`\`typescript
|
|
1282
|
+
// Prisma example:
|
|
1283
|
+
await prisma.customer.upsert({
|
|
1284
|
+
where: { email: customer.email },
|
|
1285
|
+
update: { address: JSON.stringify(address) },
|
|
1286
|
+
create: { email: customer.email, name: customer.name, address: JSON.stringify(address) },
|
|
1287
|
+
});
|
|
1288
|
+
\`\`\`
|
|
1234
1289
|
|
|
1235
1290
|
## Events
|
|
1236
1291
|
|
|
@@ -3059,9 +3114,8 @@ function stopServer() {
|
|
|
3059
3114
|
if (serverProcess) {
|
|
3060
3115
|
try {
|
|
3061
3116
|
if (process.platform === "win32" && serverProcess.pid) {
|
|
3062
|
-
(0, import_node_child_process2.
|
|
3063
|
-
stdio: "ignore"
|
|
3064
|
-
detached: true
|
|
3117
|
+
(0, import_node_child_process2.spawnSync)("taskkill", ["/F", "/T", "/PID", String(serverProcess.pid)], {
|
|
3118
|
+
stdio: "ignore"
|
|
3065
3119
|
});
|
|
3066
3120
|
} else {
|
|
3067
3121
|
serverProcess.kill("SIGTERM");
|
|
@@ -3335,7 +3389,7 @@ function validatePkcs8Pem(pem) {
|
|
|
3335
3389
|
}
|
|
3336
3390
|
async function main() {
|
|
3337
3391
|
printHeader();
|
|
3338
|
-
we("OneAddress Partner Setup
|
|
3392
|
+
we("OneAddress Partner Setup");
|
|
3339
3393
|
v2.info("We will:");
|
|
3340
3394
|
v2.info(" 1. Collect your credentials from the Partner Portal");
|
|
3341
3395
|
v2.info(" 2. Scaffold a working webhook server for your platform");
|
|
@@ -3530,9 +3584,9 @@ async function main() {
|
|
|
3530
3584
|
`Files in: ${outDir}`
|
|
3531
3585
|
].join("\n");
|
|
3532
3586
|
const nextSteps = [
|
|
3533
|
-
"1.
|
|
3534
|
-
"2.
|
|
3535
|
-
"3. For production:
|
|
3587
|
+
"1. Trigger a live event: update your address at app.oneaddress.io\n \u2192 src/store.ts will save it to data.db automatically",
|
|
3588
|
+
"2. View your database: open data.db with DB Browser for SQLite\n (free download at sqlitebrowser.org)",
|
|
3589
|
+
"3. For production: swap src/store.ts for your real database (Postgres, MySQL, etc.)\n then deploy and update the webhook URL in the portal",
|
|
3536
3590
|
"4. Questions? partners.oneaddress.io or partners@oneaddress.io"
|
|
3537
3591
|
].join("\n\n");
|
|
3538
3592
|
ye(summary, "Setup summary");
|