@oneaddress/setup 1.1.3 → 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 +130 -75
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -835,8 +835,9 @@ 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.5" : "?";
|
|
838
839
|
function printHeader() {
|
|
839
|
-
const BAR_LEN =
|
|
840
|
+
const BAR_LEN = 85;
|
|
840
841
|
const TOP_BAR = fn("\u250C") + dm("\u2500".repeat(BAR_LEN)) + fn("\u2510");
|
|
841
842
|
const BOT_BAR = fn("\u2514") + dm("\u2500".repeat(BAR_LEN)) + fn("\u2518");
|
|
842
843
|
const SIDE = dm("\u2502");
|
|
@@ -845,13 +846,13 @@ function printHeader() {
|
|
|
845
846
|
console.log(` ${SIDE}${" ".repeat(BAR_LEN)}${SIDE}`);
|
|
846
847
|
for (let i = 0; i < 7; i++) {
|
|
847
848
|
const line = wb(ONE_ROWS[i]) + " " + ab(ADDR_ROWS[i]);
|
|
848
|
-
console.log(`
|
|
849
|
+
console.log(` ${SIDE} ${line} ${SIDE}`);
|
|
849
850
|
}
|
|
850
|
-
console.log(`
|
|
851
|
+
console.log(` ${SIDE} ${fn("\u2591".repeat(81))} ${SIDE}`);
|
|
851
852
|
console.log(` ${SIDE}${" ".repeat(BAR_LEN)}${SIDE}`);
|
|
852
853
|
const wizard = mi("Partner Setup Wizard");
|
|
853
|
-
const
|
|
854
|
-
const tagLine = ` ${wizard} ${
|
|
854
|
+
const ver = fn(`v${WIZARD_VERSION} \xB7 partners.oneaddress.io`);
|
|
855
|
+
const tagLine = ` ${wizard} ${ver}`;
|
|
855
856
|
const padLen = BAR_LEN - 4 - stripAnsi(tagLine).length;
|
|
856
857
|
console.log(` ${SIDE} ${tagLine}${" ".repeat(Math.max(0, padLen))} ${SIDE}`);
|
|
857
858
|
console.log(` ${SIDE}${" ".repeat(BAR_LEN)}${SIDE}`);
|
|
@@ -926,27 +927,81 @@ dist/
|
|
|
926
927
|
},
|
|
927
928
|
"devDependencies": {
|
|
928
929
|
"@types/express": "^4.17.0",
|
|
929
|
-
"@types/node": "^
|
|
930
|
+
"@types/node": "^22.5.0",
|
|
930
931
|
"tsup": "^8.0.0",
|
|
931
932
|
"tsx": "^4.0.0",
|
|
932
933
|
"typescript": "^5.0.0"
|
|
933
934
|
},
|
|
934
|
-
"engines": { "node": ">=
|
|
935
|
+
"engines": { "node": ">=22.5" }
|
|
935
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;
|
|
936
986
|
`
|
|
937
987
|
},
|
|
938
988
|
{
|
|
939
989
|
name: "src/store.ts",
|
|
940
990
|
content: `/**
|
|
941
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
|
|
942
|
-
* \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
|
|
943
997
|
* \u2551 \u2551
|
|
944
|
-
* \u2551
|
|
945
|
-
* \u2551
|
|
946
|
-
* \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
|
|
947
1001
|
* \u2551 \u2014 the protocol layer is handled for you, never touch it. \u2551
|
|
948
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
|
|
949
1003
|
*/
|
|
1004
|
+
import db from './db.js';
|
|
950
1005
|
|
|
951
1006
|
export type Address = Record<string, unknown>;
|
|
952
1007
|
|
|
@@ -960,47 +1015,34 @@ export type VerifyResult = 'match' | 'mismatch' | 'not_found';
|
|
|
960
1015
|
/**
|
|
961
1016
|
* Called when a consumer updates their address (address.updated event).
|
|
962
1017
|
*
|
|
963
|
-
*
|
|
964
|
-
*
|
|
965
|
-
*
|
|
966
|
-
* Persist this to your database (update the customer's address record,
|
|
967
|
-
* 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.
|
|
968
1020
|
*/
|
|
969
1021
|
export async function saveAddress(customer: Customer, address: Address): Promise<void> {
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
// await fetch('https://your-crm.internal/addresses', {
|
|
988
|
-
// method: 'PUT',
|
|
989
|
-
// body: JSON.stringify({ email: customer.email, address }),
|
|
990
|
-
// });
|
|
991
|
-
//
|
|
992
|
-
// \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
|
|
993
|
-
|
|
994
|
-
console.log('[store] saveAddress \u2014 implement this function to persist the address');
|
|
995
|
-
console.log('[store] customer:', customer.email);
|
|
996
|
-
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);
|
|
997
1039
|
}
|
|
998
1040
|
|
|
999
1041
|
/**
|
|
1000
1042
|
* Called during an address verification check (address.verify event).
|
|
1001
1043
|
*
|
|
1002
|
-
*
|
|
1003
|
-
*
|
|
1044
|
+
* Looks up the customer by email and compares the incoming (decrypted)
|
|
1045
|
+
* address against what is stored in the database.
|
|
1004
1046
|
*
|
|
1005
1047
|
* Return values:
|
|
1006
1048
|
* 'match' \u2014 the address matches your records exactly
|
|
@@ -1008,19 +1050,20 @@ export async function saveAddress(customer: Customer, address: Address): Promise
|
|
|
1008
1050
|
* 'not_found' \u2014 you have no record for this customer at all
|
|
1009
1051
|
*/
|
|
1010
1052
|
export async function verifyAddress(customer: Customer, address: Address): Promise<VerifyResult> {
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
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;
|
|
1024
1067
|
}
|
|
1025
1068
|
`
|
|
1026
1069
|
},
|
|
@@ -1210,26 +1253,39 @@ npm run dev # hot-reload via tsx
|
|
|
1210
1253
|
Webhook endpoint: \`POST http://localhost:3001/webhook\`
|
|
1211
1254
|
Health check: \`GET http://localhost:3001/health\`
|
|
1212
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
|
+
|
|
1213
1259
|
## Architecture
|
|
1214
1260
|
|
|
1215
1261
|
| File | Role |
|
|
1216
1262
|
|------|------|
|
|
1217
1263
|
| \`src/server.ts\` | **Protocol layer \u2014 do not edit.** HMAC verification, timestamp replay protection, ECDH decryption, deduplication. |
|
|
1218
|
-
| \`src/
|
|
1219
|
-
|
|
1220
|
-
## 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. |
|
|
1221
1266
|
|
|
1222
|
-
|
|
1267
|
+
## Database tables
|
|
1223
1268
|
|
|
1224
|
-
\`\`\`
|
|
1225
|
-
|
|
1226
|
-
|
|
1269
|
+
\`\`\`sql
|
|
1270
|
+
-- Current address per customer (upserted on every address.updated event)
|
|
1271
|
+
addresses(email, name, address JSON, updated_at)
|
|
1227
1272
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1273
|
+
-- Full history of every address change (append-only audit trail)
|
|
1274
|
+
address_history(id, email, name, address JSON, recorded_at)
|
|
1230
1275
|
\`\`\`
|
|
1231
1276
|
|
|
1232
|
-
|
|
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
|
+
\`\`\`
|
|
1233
1289
|
|
|
1234
1290
|
## Events
|
|
1235
1291
|
|
|
@@ -3058,9 +3114,8 @@ function stopServer() {
|
|
|
3058
3114
|
if (serverProcess) {
|
|
3059
3115
|
try {
|
|
3060
3116
|
if (process.platform === "win32" && serverProcess.pid) {
|
|
3061
|
-
(0, import_node_child_process2.
|
|
3062
|
-
stdio: "ignore"
|
|
3063
|
-
detached: true
|
|
3117
|
+
(0, import_node_child_process2.spawnSync)("taskkill", ["/F", "/T", "/PID", String(serverProcess.pid)], {
|
|
3118
|
+
stdio: "ignore"
|
|
3064
3119
|
});
|
|
3065
3120
|
} else {
|
|
3066
3121
|
serverProcess.kill("SIGTERM");
|
|
@@ -3334,7 +3389,7 @@ function validatePkcs8Pem(pem) {
|
|
|
3334
3389
|
}
|
|
3335
3390
|
async function main() {
|
|
3336
3391
|
printHeader();
|
|
3337
|
-
we("OneAddress Partner Setup
|
|
3392
|
+
we("OneAddress Partner Setup");
|
|
3338
3393
|
v2.info("We will:");
|
|
3339
3394
|
v2.info(" 1. Collect your credentials from the Partner Portal");
|
|
3340
3395
|
v2.info(" 2. Scaffold a working webhook server for your platform");
|
|
@@ -3529,9 +3584,9 @@ async function main() {
|
|
|
3529
3584
|
`Files in: ${outDir}`
|
|
3530
3585
|
].join("\n");
|
|
3531
3586
|
const nextSteps = [
|
|
3532
|
-
"1.
|
|
3533
|
-
"2.
|
|
3534
|
-
"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",
|
|
3535
3590
|
"4. Questions? partners.oneaddress.io or partners@oneaddress.io"
|
|
3536
3591
|
].join("\n\n");
|
|
3537
3592
|
ye(summary, "Setup summary");
|