Adjacent work: PDF receipts, encrypted blob+share mock, NATS tunnel, SHA256 Zapier action
Around remaining blockers (Zapier login, live Verae): generate PDF receipts without extra deps; AES-256-GCM object store with tenant isolation and share tokens; scripts/nats-tunnel.sh to NS1; SHA256 Hash Text local Zapier action; scripts/test-offline.sh for the no-login suite. Learned: other-tenant object GET is 403; share token is the mock unwrap path.
This commit is contained in:
parent
c8f9d01a04
commit
645a24909a
45 changed files with 1053 additions and 24 deletions
|
|
@ -9,10 +9,10 @@ Source: [workspace-brief.md](../00-sources/workspace-brief.md). Owners after com
|
||||||
| c | Register SHA256 or return original timestamp + ref | verae middleware mock + zappier `/v1/timestamp` | **Mock done** — second create returns original `jobId` + `existing: true` | Live OpenAPI still `data` only |
|
| c | Register SHA256 or return original timestamp + ref | verae middleware mock + zappier `/v1/timestamp` | **Mock done** — second create returns original `jobId` + `existing: true` | Live OpenAPI still `data` only |
|
||||||
| d | Lookup by SHA256 | `GET /zapier/v1/hashes/{sha256}` and zappier `/v1/hashes/{sha256}` | **Mock done** | No live Verae route |
|
| d | Lookup by SHA256 | `GET /zapier/v1/hashes/{sha256}` and zappier `/v1/hashes/{sha256}` | **Mock done** | No live Verae route |
|
||||||
| e | Timestamp + public/private metadata | verae mock | Missing | No |
|
| e | Timestamp + public/private metadata | verae mock | Missing | No |
|
||||||
| f | e + encrypted LTS binary | verae mock | Missing. Do not use zappier demo `/v1/storage` | No |
|
| f | e + encrypted LTS binary | `POST/GET /zapier/v1/objects` AES-256-GCM mock | **Mock done** | No |
|
||||||
| g | Share file/dir + decryption key | verae mock | Missing | No |
|
| g | Share file/dir + decryption key | `POST .../share` + `GET /zapier/v1/shares/:token` | **File mock done**; directory tree still TBD | No |
|
||||||
| h | Retrieve SHA256 + metadata | verae status/verify | Partial by `jobId` | `GET /api/status/{jobId}`, `GET /api/verify/{jobId}` |
|
| h | Retrieve SHA256 + metadata | verae status/verify | Partial by `jobId` | `GET /api/status/{jobId}`, `GET /api/verify/{jobId}` |
|
||||||
| i | Certified retrieval receipt (PDF/JSON) extra seal | `GET /zapier/v1/receipts/{jobId}?format=json` | **JSON mock done**; PDF 501 | No |
|
| i | Certified retrieval receipt (PDF/JSON) extra seal | `GET /zapier/v1/receipts/{jobId}?format=json\|pdf` | **JSON + PDF mock done** | No |
|
||||||
| j | Setup payment method | zappier portal reload (Stripe) | Built (prepaid reload; card-on-file TBD) | No |
|
| j | Setup payment method | zappier portal reload (Stripe) | Built (prepaid reload; card-on-file TBD) | No |
|
||||||
| k | Get invoices | zappier portal + admin | Built | No |
|
| k | Get invoices | zappier portal + admin | Built | No |
|
||||||
| l | Get payment receipts | zappier invoice HTML/print | Built as invoices | No |
|
| l | Get payment receipts | zappier invoice HTML/print | Built as invoices | No |
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ These cannot finish in this session:
|
||||||
|
|
||||||
1. **`zapier-platform login` + `register` + `push`** — browser OAuth to your Zapier account. Follow [docs/04-activate/SETUP-ZAPIER-DEVELOPER.md](04-activate/SETUP-ZAPIER-DEVELOPER.md). After push, commit `.zapierapprc`.
|
1. **`zapier-platform login` + `register` + `push`** — browser OAuth to your Zapier account. Follow [docs/04-activate/SETUP-ZAPIER-DEVELOPER.md](04-activate/SETUP-ZAPIER-DEVELOPER.md). After push, commit `.zapierapprc`.
|
||||||
2. **Live `api.veraetime.net`** — hash lookup, metadata, receipts, LTS, share are **not** in the public OpenAPI. Mocks exist; live client refuses unknown paths.
|
2. **Live `api.veraetime.net`** — hash lookup, metadata, receipts, LTS, share are **not** in the public OpenAPI. Mocks exist; live client refuses unknown paths.
|
||||||
3. **PDF receipts** — JSON extra-seal works; PDF is 501.
|
3. **PDF receipts** — JSON extra-seal works; PDF is generated locally (`?format=pdf`). Legal copy/seal artwork still TBD.
|
||||||
4. **Encrypted LTS + directory share (f, g)** — not implemented beyond mock timestamp metadata.
|
4. **Encrypted LTS + share** — mock AES-256-GCM blobs + share tokens exist (`/zapier/v1/objects`, `/zapier/v1/shares/:token`). Not live Verae/Peergos.
|
||||||
5. **NS1 NATS public bind** — left loopback-only; use SSH tunnel (`14222`).
|
5. **NS1 NATS public bind** — left loopback-only; `scripts/nats-tunnel.sh` maps `14222`.
|
||||||
|
|
||||||
Everything else for morning Add Numbers is ready.
|
Everything else for morning Add Numbers is ready.
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ One MD file per source module.
|
||||||
- [verae-zapier-middleware/debug/trace](verae-zapier-middleware/debug/trace.md)
|
- [verae-zapier-middleware/debug/trace](verae-zapier-middleware/debug/trace.md)
|
||||||
- [verae-zapier-middleware/errors](verae-zapier-middleware/errors.md)
|
- [verae-zapier-middleware/errors](verae-zapier-middleware/errors.md)
|
||||||
- [verae-zapier-middleware/index](verae-zapier-middleware/index.md)
|
- [verae-zapier-middleware/index](verae-zapier-middleware/index.md)
|
||||||
|
- [verae-zapier-middleware/lib/receiptPdf](verae-zapier-middleware/lib/receiptPdf.md)
|
||||||
- [verae-zapier-middleware/lib/tokens](verae-zapier-middleware/lib/tokens.md)
|
- [verae-zapier-middleware/lib/tokens](verae-zapier-middleware/lib/tokens.md)
|
||||||
- [verae-zapier-middleware/middleware/authenticate](verae-zapier-middleware/middleware/authenticate.md)
|
- [verae-zapier-middleware/middleware/authenticate](verae-zapier-middleware/middleware/authenticate.md)
|
||||||
- [verae-zapier-middleware/middleware/rateLimit](verae-zapier-middleware/middleware/rateLimit.md)
|
- [verae-zapier-middleware/middleware/rateLimit](verae-zapier-middleware/middleware/rateLimit.md)
|
||||||
|
|
@ -47,6 +48,8 @@ One MD file per source module.
|
||||||
- [verae-zapier-middleware/routes/authRoutes](verae-zapier-middleware/routes/authRoutes.md)
|
- [verae-zapier-middleware/routes/authRoutes](verae-zapier-middleware/routes/authRoutes.md)
|
||||||
- [verae-zapier-middleware/routes/hashRoutes](verae-zapier-middleware/routes/hashRoutes.md)
|
- [verae-zapier-middleware/routes/hashRoutes](verae-zapier-middleware/routes/hashRoutes.md)
|
||||||
- [verae-zapier-middleware/routes/index](verae-zapier-middleware/routes/index.md)
|
- [verae-zapier-middleware/routes/index](verae-zapier-middleware/routes/index.md)
|
||||||
|
- [verae-zapier-middleware/routes/objectRoutes](verae-zapier-middleware/routes/objectRoutes.md)
|
||||||
|
- [verae-zapier-middleware/routes/receiptRoutes](verae-zapier-middleware/routes/receiptRoutes.md)
|
||||||
- [verae-zapier-middleware/routes/statusRoutes](verae-zapier-middleware/routes/statusRoutes.md)
|
- [verae-zapier-middleware/routes/statusRoutes](verae-zapier-middleware/routes/statusRoutes.md)
|
||||||
- [verae-zapier-middleware/routes/tenantRoutes](verae-zapier-middleware/routes/tenantRoutes.md)
|
- [verae-zapier-middleware/routes/tenantRoutes](verae-zapier-middleware/routes/tenantRoutes.md)
|
||||||
- [verae-zapier-middleware/routes/timestampRoutes](verae-zapier-middleware/routes/timestampRoutes.md)
|
- [verae-zapier-middleware/routes/timestampRoutes](verae-zapier-middleware/routes/timestampRoutes.md)
|
||||||
|
|
@ -58,6 +61,7 @@ One MD file per source module.
|
||||||
- [verae-zapier-middleware/services/timestampService](verae-zapier-middleware/services/timestampService.md)
|
- [verae-zapier-middleware/services/timestampService](verae-zapier-middleware/services/timestampService.md)
|
||||||
- [verae-zapier-middleware/services/verifyService](verae-zapier-middleware/services/verifyService.md)
|
- [verae-zapier-middleware/services/verifyService](verae-zapier-middleware/services/verifyService.md)
|
||||||
- [verae-zapier-middleware/services/webhookService](verae-zapier-middleware/services/webhookService.md)
|
- [verae-zapier-middleware/services/webhookService](verae-zapier-middleware/services/webhookService.md)
|
||||||
|
- [verae-zapier-middleware/store/blobs](verae-zapier-middleware/store/blobs.md)
|
||||||
- [verae-zapier-middleware/store/db](verae-zapier-middleware/store/db.md)
|
- [verae-zapier-middleware/store/db](verae-zapier-middleware/store/db.md)
|
||||||
- [verae-zapier-middleware/store/jobWatchers](verae-zapier-middleware/store/jobWatchers.md)
|
- [verae-zapier-middleware/store/jobWatchers](verae-zapier-middleware/store/jobWatchers.md)
|
||||||
- [verae-zapier-middleware/store/tenants](verae-zapier-middleware/store/tenants.md)
|
- [verae-zapier-middleware/store/tenants](verae-zapier-middleware/store/tenants.md)
|
||||||
|
|
@ -69,6 +73,7 @@ One MD file per source module.
|
||||||
- [verae-activate/authentication](verae-activate/authentication.md)
|
- [verae-activate/authentication](verae-activate/authentication.md)
|
||||||
- [verae-activate/creates/add_numbers](verae-activate/creates/add_numbers.md)
|
- [verae-activate/creates/add_numbers](verae-activate/creates/add_numbers.md)
|
||||||
- [verae-activate/creates/echo](verae-activate/creates/echo.md)
|
- [verae-activate/creates/echo](verae-activate/creates/echo.md)
|
||||||
|
- [verae-activate/creates/sha256](verae-activate/creates/sha256.md)
|
||||||
- [verae-activate/index](verae-activate/index.md)
|
- [verae-activate/index](verae-activate/index.md)
|
||||||
- [verae-zapier/authentication](verae-zapier/authentication.md)
|
- [verae-zapier/authentication](verae-zapier/authentication.md)
|
||||||
- [verae-zapier/creates/add_numbers](verae-zapier/creates/add_numbers.md)
|
- [verae-zapier/creates/add_numbers](verae-zapier/creates/add_numbers.md)
|
||||||
|
|
|
||||||
40
docs/modules/verae-activate/creates/sha256.md
Normal file
40
docs/modules/verae-activate/creates/sha256.md
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# `verae-activate/creates/sha256`
|
||||||
|
|
||||||
|
**Package:** `verae-activate`
|
||||||
|
**Source:** `packages/verae-activate/creates/sha256.js`
|
||||||
|
**Lines:** 37
|
||||||
|
|
||||||
|
## What this module is
|
||||||
|
|
||||||
|
Implementation module in `verae-activate`. The tables below are extracted from the source (signatures + JSDoc).
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
`key`, `noun`, `display`, `label`, `description`
|
||||||
|
|
||||||
|
## Types / interfaces / classes
|
||||||
|
|
||||||
|
_None extracted._
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) |
|
||||||
|
|------|------------|---------------------|---------|-----------------------------|
|
||||||
|
| `perform` | `_z, bundle` | — | `unknown` | see Call graph |
|
||||||
|
|
||||||
|
## What it imports / requires
|
||||||
|
|
||||||
|
- `node:crypto`
|
||||||
|
|
||||||
|
## Call graph (identifiers invoked)
|
||||||
|
|
||||||
|
`async`, `createHash`, `update`, `digest`
|
||||||
|
|
||||||
|
Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types.
|
||||||
|
|
||||||
|
## Return values (how to read this)
|
||||||
|
|
||||||
|
- HTTP route handlers return Express `res.json(...)` bodies (see route docs).
|
||||||
|
- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches).
|
||||||
|
- Pricing functions return integer **cents** on `Quote.totalCents`.
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**Package:** `verae-activate`
|
**Package:** `verae-activate`
|
||||||
**Source:** `packages/verae-activate/index.js`
|
**Source:** `packages/verae-activate/index.js`
|
||||||
**Lines:** 23
|
**Lines:** 25
|
||||||
|
|
||||||
## What this module is
|
## What this module is
|
||||||
|
|
||||||
|
|
@ -10,7 +10,7 @@ Implementation module in `verae-activate`. The tables below are extracted from t
|
||||||
|
|
||||||
## Exports
|
## Exports
|
||||||
|
|
||||||
`version`, `platformVersion`, `authentication`, `creates`, `addNumbers`, `echo`
|
`version`, `platformVersion`, `authentication`, `creates`, `addNumbers`, `echo`, `sha256Hash`
|
||||||
|
|
||||||
## Types / interfaces / classes
|
## Types / interfaces / classes
|
||||||
|
|
||||||
|
|
@ -25,6 +25,7 @@ _No top-level functions extracted._
|
||||||
- `./authentication`
|
- `./authentication`
|
||||||
- `./creates/add_numbers`
|
- `./creates/add_numbers`
|
||||||
- `./creates/echo`
|
- `./creates/echo`
|
||||||
|
- `./creates/sha256`
|
||||||
- `./package.json`
|
- `./package.json`
|
||||||
- `zapier-platform-core`
|
- `zapier-platform-core`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**Package:** `verae-zapier-middleware`
|
**Package:** `verae-zapier-middleware`
|
||||||
**Source:** `packages/verae-zapier-middleware/src/clients/veraeClient.js`
|
**Source:** `packages/verae-zapier-middleware/src/clients/veraeClient.js`
|
||||||
**Lines:** 351
|
**Lines:** 352
|
||||||
|
|
||||||
## What this module is
|
## What this module is
|
||||||
|
|
||||||
|
|
|
||||||
41
docs/modules/verae-zapier-middleware/lib/receiptPdf.md
Normal file
41
docs/modules/verae-zapier-middleware/lib/receiptPdf.md
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
# `verae-zapier-middleware/lib/receiptPdf`
|
||||||
|
|
||||||
|
**Package:** `verae-zapier-middleware`
|
||||||
|
**Source:** `packages/verae-zapier-middleware/src/lib/receiptPdf.js`
|
||||||
|
**Lines:** 65
|
||||||
|
|
||||||
|
## What this module is
|
||||||
|
|
||||||
|
Implementation module in `verae-zapier-middleware`. The tables below are extracted from the source (signatures + JSDoc).
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
`buildReceiptPdf`
|
||||||
|
|
||||||
|
## Types / interfaces / classes
|
||||||
|
|
||||||
|
_None extracted._
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) |
|
||||||
|
|------|------------|---------------------|---------|-----------------------------|
|
||||||
|
| `pdfEscape` | `s` | s: `string` | `string` | see Call graph |
|
||||||
|
| `buildReceiptPdf` | `receipt` | receipt: `object` | `Buffer` | see Call graph |
|
||||||
|
|
||||||
|
## What it imports / requires
|
||||||
|
|
||||||
|
_No imports detected._
|
||||||
|
|
||||||
|
## Call graph (identifiers invoked)
|
||||||
|
|
||||||
|
`receipts`, `pdfEscape`, `replace`, `buildReceiptPdf`, `slice`, `map`, `join`, `byteLength`, `push`, `padStart`, `from`
|
||||||
|
|
||||||
|
Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types.
|
||||||
|
|
||||||
|
## Return values (how to read this)
|
||||||
|
|
||||||
|
- HTTP route handlers return Express `res.json(...)` bodies (see route docs).
|
||||||
|
- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches).
|
||||||
|
- Pricing functions return integer **cents** on `Quote.totalCents`.
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**Package:** `verae-zapier-middleware`
|
**Package:** `verae-zapier-middleware`
|
||||||
**Source:** `packages/verae-zapier-middleware/src/routes/index.js`
|
**Source:** `packages/verae-zapier-middleware/src/routes/index.js`
|
||||||
**Lines:** 36
|
**Lines:** 41
|
||||||
|
|
||||||
## What this module is
|
## What this module is
|
||||||
|
|
||||||
|
|
@ -30,6 +30,8 @@ _No top-level functions extracted._
|
||||||
- `./webhookRoutes.js`
|
- `./webhookRoutes.js`
|
||||||
- `./tenantRoutes.js`
|
- `./tenantRoutes.js`
|
||||||
- `./hashRoutes.js`
|
- `./hashRoutes.js`
|
||||||
|
- `./receiptRoutes.js`
|
||||||
|
- `./objectRoutes.js`
|
||||||
- `../middleware/authenticate.js`
|
- `../middleware/authenticate.js`
|
||||||
- `../middleware/rateLimit.js`
|
- `../middleware/rateLimit.js`
|
||||||
|
|
||||||
|
|
|
||||||
50
docs/modules/verae-zapier-middleware/routes/objectRoutes.md
Normal file
50
docs/modules/verae-zapier-middleware/routes/objectRoutes.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# `verae-zapier-middleware/routes/objectRoutes`
|
||||||
|
|
||||||
|
**Package:** `verae-zapier-middleware`
|
||||||
|
**Source:** `packages/verae-zapier-middleware/src/routes/objectRoutes.js`
|
||||||
|
**Lines:** 91
|
||||||
|
|
||||||
|
## What this module is
|
||||||
|
|
||||||
|
Implementation module in `verae-zapier-middleware`. The tables below are extracted from the source (signatures + JSDoc).
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
`objectRoutes`, `publicShareRoutes`
|
||||||
|
|
||||||
|
## Types / interfaces / classes
|
||||||
|
|
||||||
|
_None extracted._
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
_No top-level functions extracted._
|
||||||
|
|
||||||
|
## Methods (class / object)
|
||||||
|
|
||||||
|
| Name | Parameters |
|
||||||
|
|------|------------|
|
||||||
|
| `asyncHandler` | `async (req, res` |
|
||||||
|
| `asyncHandler` | `async (req, res` |
|
||||||
|
| `asyncHandler` | `async (req, res` |
|
||||||
|
| `asyncHandler` | `async (req, res` |
|
||||||
|
|
||||||
|
## What it imports / requires
|
||||||
|
|
||||||
|
- `express`
|
||||||
|
- `../errors.js`
|
||||||
|
- `../store/blobs.js`
|
||||||
|
- `../services/timestampService.js`
|
||||||
|
|
||||||
|
## Call graph (identifiers invoked)
|
||||||
|
|
||||||
|
`post`, `asyncHandler`, `async`, `from`, `createTimestamp`, `toString`, `slice`, `putBlob`, `status`, `json`, `get`, `readBlobPlaintext`, `createShare`, `redeemShare`
|
||||||
|
|
||||||
|
Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types.
|
||||||
|
|
||||||
|
## Return values (how to read this)
|
||||||
|
|
||||||
|
- HTTP route handlers return Express `res.json(...)` bodies (see route docs).
|
||||||
|
- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches).
|
||||||
|
- Pricing functions return integer **cents** on `Quote.totalCents`.
|
||||||
|
|
||||||
47
docs/modules/verae-zapier-middleware/routes/receiptRoutes.md
Normal file
47
docs/modules/verae-zapier-middleware/routes/receiptRoutes.md
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
# `verae-zapier-middleware/routes/receiptRoutes`
|
||||||
|
|
||||||
|
**Package:** `verae-zapier-middleware`
|
||||||
|
**Source:** `packages/verae-zapier-middleware/src/routes/receiptRoutes.js`
|
||||||
|
**Lines:** 46
|
||||||
|
|
||||||
|
## What this module is
|
||||||
|
|
||||||
|
Implementation module in `verae-zapier-middleware`. The tables below are extracted from the source (signatures + JSDoc).
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
`receiptRoutes`
|
||||||
|
|
||||||
|
## Types / interfaces / classes
|
||||||
|
|
||||||
|
_None extracted._
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
_No top-level functions extracted._
|
||||||
|
|
||||||
|
## Methods (class / object)
|
||||||
|
|
||||||
|
| Name | Parameters |
|
||||||
|
|------|------------|
|
||||||
|
| `asyncHandler` | `async (req, res` |
|
||||||
|
|
||||||
|
## What it imports / requires
|
||||||
|
|
||||||
|
- `express`
|
||||||
|
- `../errors.js`
|
||||||
|
- `../services/timestampService.js`
|
||||||
|
- `../lib/receiptPdf.js`
|
||||||
|
|
||||||
|
## Call graph (identifiers invoked)
|
||||||
|
|
||||||
|
`receipt`, `get`, `asyncHandler`, `async`, `toLowerCase`, `getJobStatus`, `toISOString`, `buildReceiptPdf`, `setHeader`, `send`, `json`
|
||||||
|
|
||||||
|
Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types.
|
||||||
|
|
||||||
|
## Return values (how to read this)
|
||||||
|
|
||||||
|
- HTTP route handlers return Express `res.json(...)` bodies (see route docs).
|
||||||
|
- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches).
|
||||||
|
- Pricing functions return integer **cents** on `Quote.totalCents`.
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**Package:** `verae-zapier-middleware`
|
**Package:** `verae-zapier-middleware`
|
||||||
**Source:** `packages/verae-zapier-middleware/src/routes/statusRoutes.js`
|
**Source:** `packages/verae-zapier-middleware/src/routes/statusRoutes.js`
|
||||||
**Lines:** 47
|
**Lines:** 52
|
||||||
|
|
||||||
## What this module is
|
## What this module is
|
||||||
|
|
||||||
|
|
|
||||||
55
docs/modules/verae-zapier-middleware/store/blobs.md
Normal file
55
docs/modules/verae-zapier-middleware/store/blobs.md
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
# `verae-zapier-middleware/store/blobs`
|
||||||
|
|
||||||
|
**Package:** `verae-zapier-middleware`
|
||||||
|
**Source:** `packages/verae-zapier-middleware/src/store/blobs.js`
|
||||||
|
**Lines:** 137
|
||||||
|
|
||||||
|
## What this module is
|
||||||
|
|
||||||
|
Implementation module in `verae-zapier-middleware`. The tables below are extracted from the source (signatures + JSDoc).
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
`decryptBlob`, `putBlob`, `getBlobRecord`, `readBlobPlaintext`, `createShare`, `redeemShare`
|
||||||
|
|
||||||
|
## Types / interfaces / classes
|
||||||
|
|
||||||
|
_None extracted._
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) |
|
||||||
|
|------|------------|---------------------|---------|-----------------------------|
|
||||||
|
| `ensure` | `(none)` | — | `unknown` | see Call graph |
|
||||||
|
| `encrypt` | `plaintext, key` | plaintext: `Buffer`, key: `Buffer` | `unknown` | see Call graph |
|
||||||
|
| `decryptBlob` | `enc, key` | key: `Buffer` | `unknown` | see Call graph |
|
||||||
|
| `putBlob` | `{ tenantId, sha256, filename, bytes, publicMetadata = {}, privateMetadata = {} }` | params: `object`, params.tenantId: `string`, params.sha256: `string`, params.filename: `string`, params.bytes: `Buffer`, params.publicMetadata: `object`, params.privateMetadata: `object` | `unknown` | see Call graph |
|
||||||
|
| `getBlobRecord` | `id` | — | `unknown` | see Call graph |
|
||||||
|
| `readBlobPlaintext` | `id, tenantId` | id: `string`, tenantId: `string` | `unknown` | see Call graph |
|
||||||
|
| `createShare` | `{ blobId, fromTenantId, label }` | — | `unknown` | see Call graph |
|
||||||
|
| `redeemShare` | `token` | token: `string` | `unknown` | see Call graph |
|
||||||
|
|
||||||
|
## Methods (class / object)
|
||||||
|
|
||||||
|
| Name | Parameters |
|
||||||
|
|------|------------|
|
||||||
|
| `persist` | `(none)` |
|
||||||
|
| `persist` | `(none)` |
|
||||||
|
|
||||||
|
## What it imports / requires
|
||||||
|
|
||||||
|
- `node:crypto`
|
||||||
|
- `./db.js`
|
||||||
|
|
||||||
|
## Call graph (identifiers invoked)
|
||||||
|
|
||||||
|
`ensure`, `getStore`, `encrypt`, `randomBytes`, `createCipheriv`, `concat`, `update`, `final`, `getAuthTag`, `toString`, `decryptBlob`, `createDecipheriv`, `from`, `setAuthTag`, `putBlob`, `randomUUID`, `toISOString`, `persist`, `getBlobRecord`, `readBlobPlaintext`, `createShare`, `redeemShare`
|
||||||
|
|
||||||
|
Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types.
|
||||||
|
|
||||||
|
## Return values (how to read this)
|
||||||
|
|
||||||
|
- HTTP route handlers return Express `res.json(...)` bodies (see route docs).
|
||||||
|
- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches).
|
||||||
|
- Pricing functions return integer **cents** on `Quote.totalCents`.
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**Package:** `verae-zapier-middleware`
|
**Package:** `verae-zapier-middleware`
|
||||||
**Source:** `packages/verae-zapier-middleware/src/store/db.js`
|
**Source:** `packages/verae-zapier-middleware/src/store/db.js`
|
||||||
**Lines:** 115
|
**Lines:** 117
|
||||||
|
|
||||||
## What this module is
|
## What this module is
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ Verae × Zapier module reference
|
||||||
modules/verae-zapier-middleware/debug/trace
|
modules/verae-zapier-middleware/debug/trace
|
||||||
modules/verae-zapier-middleware/errors
|
modules/verae-zapier-middleware/errors
|
||||||
modules/verae-zapier-middleware/index
|
modules/verae-zapier-middleware/index
|
||||||
|
modules/verae-zapier-middleware/lib/receiptPdf
|
||||||
modules/verae-zapier-middleware/lib/tokens
|
modules/verae-zapier-middleware/lib/tokens
|
||||||
modules/verae-zapier-middleware/middleware/authenticate
|
modules/verae-zapier-middleware/middleware/authenticate
|
||||||
modules/verae-zapier-middleware/middleware/rateLimit
|
modules/verae-zapier-middleware/middleware/rateLimit
|
||||||
|
|
@ -50,6 +51,8 @@ Verae × Zapier module reference
|
||||||
modules/verae-zapier-middleware/routes/authRoutes
|
modules/verae-zapier-middleware/routes/authRoutes
|
||||||
modules/verae-zapier-middleware/routes/hashRoutes
|
modules/verae-zapier-middleware/routes/hashRoutes
|
||||||
modules/verae-zapier-middleware/routes/index
|
modules/verae-zapier-middleware/routes/index
|
||||||
|
modules/verae-zapier-middleware/routes/objectRoutes
|
||||||
|
modules/verae-zapier-middleware/routes/receiptRoutes
|
||||||
modules/verae-zapier-middleware/routes/statusRoutes
|
modules/verae-zapier-middleware/routes/statusRoutes
|
||||||
modules/verae-zapier-middleware/routes/tenantRoutes
|
modules/verae-zapier-middleware/routes/tenantRoutes
|
||||||
modules/verae-zapier-middleware/routes/timestampRoutes
|
modules/verae-zapier-middleware/routes/timestampRoutes
|
||||||
|
|
@ -61,6 +64,7 @@ Verae × Zapier module reference
|
||||||
modules/verae-zapier-middleware/services/timestampService
|
modules/verae-zapier-middleware/services/timestampService
|
||||||
modules/verae-zapier-middleware/services/verifyService
|
modules/verae-zapier-middleware/services/verifyService
|
||||||
modules/verae-zapier-middleware/services/webhookService
|
modules/verae-zapier-middleware/services/webhookService
|
||||||
|
modules/verae-zapier-middleware/store/blobs
|
||||||
modules/verae-zapier-middleware/store/db
|
modules/verae-zapier-middleware/store/db
|
||||||
modules/verae-zapier-middleware/store/jobWatchers
|
modules/verae-zapier-middleware/store/jobWatchers
|
||||||
modules/verae-zapier-middleware/store/tenants
|
modules/verae-zapier-middleware/store/tenants
|
||||||
|
|
@ -72,6 +76,7 @@ Verae × Zapier module reference
|
||||||
modules/verae-activate/authentication
|
modules/verae-activate/authentication
|
||||||
modules/verae-activate/creates/add_numbers
|
modules/verae-activate/creates/add_numbers
|
||||||
modules/verae-activate/creates/echo
|
modules/verae-activate/creates/echo
|
||||||
|
modules/verae-activate/creates/sha256
|
||||||
modules/verae-activate/index
|
modules/verae-activate/index
|
||||||
modules/verae-zapier/authentication
|
modules/verae-zapier/authentication
|
||||||
modules/verae-zapier/creates/add_numbers
|
modules/verae-zapier/creates/add_numbers
|
||||||
|
|
|
||||||
40
docs/sphinx/modules/verae-activate/creates/sha256.md
Normal file
40
docs/sphinx/modules/verae-activate/creates/sha256.md
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# `verae-activate/creates/sha256`
|
||||||
|
|
||||||
|
**Package:** `verae-activate`
|
||||||
|
**Source:** `packages/verae-activate/creates/sha256.js`
|
||||||
|
**Lines:** 37
|
||||||
|
|
||||||
|
## What this module is
|
||||||
|
|
||||||
|
Implementation module in `verae-activate`. The tables below are extracted from the source (signatures + JSDoc).
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
`key`, `noun`, `display`, `label`, `description`
|
||||||
|
|
||||||
|
## Types / interfaces / classes
|
||||||
|
|
||||||
|
_None extracted._
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) |
|
||||||
|
|------|------------|---------------------|---------|-----------------------------|
|
||||||
|
| `perform` | `_z, bundle` | — | `unknown` | see Call graph |
|
||||||
|
|
||||||
|
## What it imports / requires
|
||||||
|
|
||||||
|
- `node:crypto`
|
||||||
|
|
||||||
|
## Call graph (identifiers invoked)
|
||||||
|
|
||||||
|
`async`, `createHash`, `update`, `digest`
|
||||||
|
|
||||||
|
Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types.
|
||||||
|
|
||||||
|
## Return values (how to read this)
|
||||||
|
|
||||||
|
- HTTP route handlers return Express `res.json(...)` bodies (see route docs).
|
||||||
|
- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches).
|
||||||
|
- Pricing functions return integer **cents** on `Quote.totalCents`.
|
||||||
|
|
||||||
7
docs/sphinx/modules/verae-activate/creates/sha256.rst
Normal file
7
docs/sphinx/modules/verae-activate/creates/sha256.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
verae-activate.creates.sha256
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Generated API sheet for ``verae-activate/creates/sha256``.
|
||||||
|
|
||||||
|
.. include:: sha256.md
|
||||||
|
:literal:
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**Package:** `verae-activate`
|
**Package:** `verae-activate`
|
||||||
**Source:** `packages/verae-activate/index.js`
|
**Source:** `packages/verae-activate/index.js`
|
||||||
**Lines:** 23
|
**Lines:** 25
|
||||||
|
|
||||||
## What this module is
|
## What this module is
|
||||||
|
|
||||||
|
|
@ -10,7 +10,7 @@ Implementation module in `verae-activate`. The tables below are extracted from t
|
||||||
|
|
||||||
## Exports
|
## Exports
|
||||||
|
|
||||||
`version`, `platformVersion`, `authentication`, `creates`, `addNumbers`, `echo`
|
`version`, `platformVersion`, `authentication`, `creates`, `addNumbers`, `echo`, `sha256Hash`
|
||||||
|
|
||||||
## Types / interfaces / classes
|
## Types / interfaces / classes
|
||||||
|
|
||||||
|
|
@ -25,6 +25,7 @@ _No top-level functions extracted._
|
||||||
- `./authentication`
|
- `./authentication`
|
||||||
- `./creates/add_numbers`
|
- `./creates/add_numbers`
|
||||||
- `./creates/echo`
|
- `./creates/echo`
|
||||||
|
- `./creates/sha256`
|
||||||
- `./package.json`
|
- `./package.json`
|
||||||
- `zapier-platform-core`
|
- `zapier-platform-core`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**Package:** `verae-zapier-middleware`
|
**Package:** `verae-zapier-middleware`
|
||||||
**Source:** `packages/verae-zapier-middleware/src/clients/veraeClient.js`
|
**Source:** `packages/verae-zapier-middleware/src/clients/veraeClient.js`
|
||||||
**Lines:** 351
|
**Lines:** 352
|
||||||
|
|
||||||
## What this module is
|
## What this module is
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
# `verae-zapier-middleware/lib/receiptPdf`
|
||||||
|
|
||||||
|
**Package:** `verae-zapier-middleware`
|
||||||
|
**Source:** `packages/verae-zapier-middleware/src/lib/receiptPdf.js`
|
||||||
|
**Lines:** 65
|
||||||
|
|
||||||
|
## What this module is
|
||||||
|
|
||||||
|
Implementation module in `verae-zapier-middleware`. The tables below are extracted from the source (signatures + JSDoc).
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
`buildReceiptPdf`
|
||||||
|
|
||||||
|
## Types / interfaces / classes
|
||||||
|
|
||||||
|
_None extracted._
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) |
|
||||||
|
|------|------------|---------------------|---------|-----------------------------|
|
||||||
|
| `pdfEscape` | `s` | s: `string` | `string` | see Call graph |
|
||||||
|
| `buildReceiptPdf` | `receipt` | receipt: `object` | `Buffer` | see Call graph |
|
||||||
|
|
||||||
|
## What it imports / requires
|
||||||
|
|
||||||
|
_No imports detected._
|
||||||
|
|
||||||
|
## Call graph (identifiers invoked)
|
||||||
|
|
||||||
|
`receipts`, `pdfEscape`, `replace`, `buildReceiptPdf`, `slice`, `map`, `join`, `byteLength`, `push`, `padStart`, `from`
|
||||||
|
|
||||||
|
Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types.
|
||||||
|
|
||||||
|
## Return values (how to read this)
|
||||||
|
|
||||||
|
- HTTP route handlers return Express `res.json(...)` bodies (see route docs).
|
||||||
|
- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches).
|
||||||
|
- Pricing functions return integer **cents** on `Quote.totalCents`.
|
||||||
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
verae-zapier-middleware.lib.receiptPdf
|
||||||
|
======================================
|
||||||
|
|
||||||
|
Generated API sheet for ``verae-zapier-middleware/lib/receiptPdf``.
|
||||||
|
|
||||||
|
.. include:: receiptPdf.md
|
||||||
|
:literal:
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**Package:** `verae-zapier-middleware`
|
**Package:** `verae-zapier-middleware`
|
||||||
**Source:** `packages/verae-zapier-middleware/src/routes/index.js`
|
**Source:** `packages/verae-zapier-middleware/src/routes/index.js`
|
||||||
**Lines:** 36
|
**Lines:** 41
|
||||||
|
|
||||||
## What this module is
|
## What this module is
|
||||||
|
|
||||||
|
|
@ -30,6 +30,8 @@ _No top-level functions extracted._
|
||||||
- `./webhookRoutes.js`
|
- `./webhookRoutes.js`
|
||||||
- `./tenantRoutes.js`
|
- `./tenantRoutes.js`
|
||||||
- `./hashRoutes.js`
|
- `./hashRoutes.js`
|
||||||
|
- `./receiptRoutes.js`
|
||||||
|
- `./objectRoutes.js`
|
||||||
- `../middleware/authenticate.js`
|
- `../middleware/authenticate.js`
|
||||||
- `../middleware/rateLimit.js`
|
- `../middleware/rateLimit.js`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
# `verae-zapier-middleware/routes/objectRoutes`
|
||||||
|
|
||||||
|
**Package:** `verae-zapier-middleware`
|
||||||
|
**Source:** `packages/verae-zapier-middleware/src/routes/objectRoutes.js`
|
||||||
|
**Lines:** 91
|
||||||
|
|
||||||
|
## What this module is
|
||||||
|
|
||||||
|
Implementation module in `verae-zapier-middleware`. The tables below are extracted from the source (signatures + JSDoc).
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
`objectRoutes`, `publicShareRoutes`
|
||||||
|
|
||||||
|
## Types / interfaces / classes
|
||||||
|
|
||||||
|
_None extracted._
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
_No top-level functions extracted._
|
||||||
|
|
||||||
|
## Methods (class / object)
|
||||||
|
|
||||||
|
| Name | Parameters |
|
||||||
|
|------|------------|
|
||||||
|
| `asyncHandler` | `async (req, res` |
|
||||||
|
| `asyncHandler` | `async (req, res` |
|
||||||
|
| `asyncHandler` | `async (req, res` |
|
||||||
|
| `asyncHandler` | `async (req, res` |
|
||||||
|
|
||||||
|
## What it imports / requires
|
||||||
|
|
||||||
|
- `express`
|
||||||
|
- `../errors.js`
|
||||||
|
- `../store/blobs.js`
|
||||||
|
- `../services/timestampService.js`
|
||||||
|
|
||||||
|
## Call graph (identifiers invoked)
|
||||||
|
|
||||||
|
`post`, `asyncHandler`, `async`, `from`, `createTimestamp`, `toString`, `slice`, `putBlob`, `status`, `json`, `get`, `readBlobPlaintext`, `createShare`, `redeemShare`
|
||||||
|
|
||||||
|
Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types.
|
||||||
|
|
||||||
|
## Return values (how to read this)
|
||||||
|
|
||||||
|
- HTTP route handlers return Express `res.json(...)` bodies (see route docs).
|
||||||
|
- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches).
|
||||||
|
- Pricing functions return integer **cents** on `Quote.totalCents`.
|
||||||
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
verae-zapier-middleware.routes.objectRoutes
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
Generated API sheet for ``verae-zapier-middleware/routes/objectRoutes``.
|
||||||
|
|
||||||
|
.. include:: objectRoutes.md
|
||||||
|
:literal:
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
# `verae-zapier-middleware/routes/receiptRoutes`
|
||||||
|
|
||||||
|
**Package:** `verae-zapier-middleware`
|
||||||
|
**Source:** `packages/verae-zapier-middleware/src/routes/receiptRoutes.js`
|
||||||
|
**Lines:** 46
|
||||||
|
|
||||||
|
## What this module is
|
||||||
|
|
||||||
|
Implementation module in `verae-zapier-middleware`. The tables below are extracted from the source (signatures + JSDoc).
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
`receiptRoutes`
|
||||||
|
|
||||||
|
## Types / interfaces / classes
|
||||||
|
|
||||||
|
_None extracted._
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
_No top-level functions extracted._
|
||||||
|
|
||||||
|
## Methods (class / object)
|
||||||
|
|
||||||
|
| Name | Parameters |
|
||||||
|
|------|------------|
|
||||||
|
| `asyncHandler` | `async (req, res` |
|
||||||
|
|
||||||
|
## What it imports / requires
|
||||||
|
|
||||||
|
- `express`
|
||||||
|
- `../errors.js`
|
||||||
|
- `../services/timestampService.js`
|
||||||
|
- `../lib/receiptPdf.js`
|
||||||
|
|
||||||
|
## Call graph (identifiers invoked)
|
||||||
|
|
||||||
|
`receipt`, `get`, `asyncHandler`, `async`, `toLowerCase`, `getJobStatus`, `toISOString`, `buildReceiptPdf`, `setHeader`, `send`, `json`
|
||||||
|
|
||||||
|
Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types.
|
||||||
|
|
||||||
|
## Return values (how to read this)
|
||||||
|
|
||||||
|
- HTTP route handlers return Express `res.json(...)` bodies (see route docs).
|
||||||
|
- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches).
|
||||||
|
- Pricing functions return integer **cents** on `Quote.totalCents`.
|
||||||
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
verae-zapier-middleware.routes.receiptRoutes
|
||||||
|
============================================
|
||||||
|
|
||||||
|
Generated API sheet for ``verae-zapier-middleware/routes/receiptRoutes``.
|
||||||
|
|
||||||
|
.. include:: receiptRoutes.md
|
||||||
|
:literal:
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**Package:** `verae-zapier-middleware`
|
**Package:** `verae-zapier-middleware`
|
||||||
**Source:** `packages/verae-zapier-middleware/src/routes/statusRoutes.js`
|
**Source:** `packages/verae-zapier-middleware/src/routes/statusRoutes.js`
|
||||||
**Lines:** 47
|
**Lines:** 52
|
||||||
|
|
||||||
## What this module is
|
## What this module is
|
||||||
|
|
||||||
|
|
|
||||||
55
docs/sphinx/modules/verae-zapier-middleware/store/blobs.md
Normal file
55
docs/sphinx/modules/verae-zapier-middleware/store/blobs.md
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
# `verae-zapier-middleware/store/blobs`
|
||||||
|
|
||||||
|
**Package:** `verae-zapier-middleware`
|
||||||
|
**Source:** `packages/verae-zapier-middleware/src/store/blobs.js`
|
||||||
|
**Lines:** 137
|
||||||
|
|
||||||
|
## What this module is
|
||||||
|
|
||||||
|
Implementation module in `verae-zapier-middleware`. The tables below are extracted from the source (signatures + JSDoc).
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
`decryptBlob`, `putBlob`, `getBlobRecord`, `readBlobPlaintext`, `createShare`, `redeemShare`
|
||||||
|
|
||||||
|
## Types / interfaces / classes
|
||||||
|
|
||||||
|
_None extracted._
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) |
|
||||||
|
|------|------------|---------------------|---------|-----------------------------|
|
||||||
|
| `ensure` | `(none)` | — | `unknown` | see Call graph |
|
||||||
|
| `encrypt` | `plaintext, key` | plaintext: `Buffer`, key: `Buffer` | `unknown` | see Call graph |
|
||||||
|
| `decryptBlob` | `enc, key` | key: `Buffer` | `unknown` | see Call graph |
|
||||||
|
| `putBlob` | `{ tenantId, sha256, filename, bytes, publicMetadata = {}, privateMetadata = {} }` | params: `object`, params.tenantId: `string`, params.sha256: `string`, params.filename: `string`, params.bytes: `Buffer`, params.publicMetadata: `object`, params.privateMetadata: `object` | `unknown` | see Call graph |
|
||||||
|
| `getBlobRecord` | `id` | — | `unknown` | see Call graph |
|
||||||
|
| `readBlobPlaintext` | `id, tenantId` | id: `string`, tenantId: `string` | `unknown` | see Call graph |
|
||||||
|
| `createShare` | `{ blobId, fromTenantId, label }` | — | `unknown` | see Call graph |
|
||||||
|
| `redeemShare` | `token` | token: `string` | `unknown` | see Call graph |
|
||||||
|
|
||||||
|
## Methods (class / object)
|
||||||
|
|
||||||
|
| Name | Parameters |
|
||||||
|
|------|------------|
|
||||||
|
| `persist` | `(none)` |
|
||||||
|
| `persist` | `(none)` |
|
||||||
|
|
||||||
|
## What it imports / requires
|
||||||
|
|
||||||
|
- `node:crypto`
|
||||||
|
- `./db.js`
|
||||||
|
|
||||||
|
## Call graph (identifiers invoked)
|
||||||
|
|
||||||
|
`ensure`, `getStore`, `encrypt`, `randomBytes`, `createCipheriv`, `concat`, `update`, `final`, `getAuthTag`, `toString`, `decryptBlob`, `createDecipheriv`, `from`, `setAuthTag`, `putBlob`, `randomUUID`, `toISOString`, `persist`, `getBlobRecord`, `readBlobPlaintext`, `createShare`, `redeemShare`
|
||||||
|
|
||||||
|
Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types.
|
||||||
|
|
||||||
|
## Return values (how to read this)
|
||||||
|
|
||||||
|
- HTTP route handlers return Express `res.json(...)` bodies (see route docs).
|
||||||
|
- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches).
|
||||||
|
- Pricing functions return integer **cents** on `Quote.totalCents`.
|
||||||
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
verae-zapier-middleware.store.blobs
|
||||||
|
===================================
|
||||||
|
|
||||||
|
Generated API sheet for ``verae-zapier-middleware/store/blobs``.
|
||||||
|
|
||||||
|
.. include:: blobs.md
|
||||||
|
:literal:
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**Package:** `verae-zapier-middleware`
|
**Package:** `verae-zapier-middleware`
|
||||||
**Source:** `packages/verae-zapier-middleware/src/store/db.js`
|
**Source:** `packages/verae-zapier-middleware/src/store/db.js`
|
||||||
**Lines:** 115
|
**Lines:** 117
|
||||||
|
|
||||||
## What this module is
|
## What this module is
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@
|
||||||
"test:activate": "npm --prefix packages/verae-activate test",
|
"test:activate": "npm --prefix packages/verae-activate test",
|
||||||
"docs:modules": "python3 scripts/gen-module-docs.py",
|
"docs:modules": "python3 scripts/gen-module-docs.py",
|
||||||
"docs:sphinx": "sphinx-build -b html docs/sphinx docs/sphinx/_build/html",
|
"docs:sphinx": "sphinx-build -b html docs/sphinx docs/sphinx/_build/html",
|
||||||
"docs:pdf": "python3 scripts/gen-module-pdfs.py"
|
"docs:pdf": "python3 scripts/gen-module-pdfs.py",
|
||||||
|
"test:offline": "bash scripts/test-offline.sh",
|
||||||
|
"nats:tunnel": "bash scripts/nats-tunnel.sh"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
|
|
|
||||||
36
packages/verae-activate/creates/sha256.js
Normal file
36
packages/verae-activate/creates/sha256.js
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
* SHA256 of a string — local, no API. Lets you hash a file/text in a Zap.
|
||||||
|
* @module creates/sha256
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { createHash } = require('node:crypto');
|
||||||
|
|
||||||
|
const perform = async (_z, bundle) => {
|
||||||
|
const text = String(bundle.inputData.text ?? '');
|
||||||
|
const sha256 = createHash('sha256').update(text, 'utf8').digest('hex');
|
||||||
|
return { textLength: text.length, sha256, mode: 'local' };
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
key: 'sha256_hash',
|
||||||
|
noun: 'Hash',
|
||||||
|
display: {
|
||||||
|
label: 'SHA256 Hash Text',
|
||||||
|
description: 'Computes SHA256 hex of text inside Zapier. No Verae call.',
|
||||||
|
},
|
||||||
|
operation: {
|
||||||
|
cleanInputData: false,
|
||||||
|
inputFields: [{ key: 'text', label: 'Text', type: 'text', required: true }],
|
||||||
|
perform,
|
||||||
|
sample: {
|
||||||
|
textLength: 5,
|
||||||
|
sha256: '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
|
||||||
|
mode: 'local',
|
||||||
|
},
|
||||||
|
outputFields: [
|
||||||
|
{ key: 'sha256', type: 'string' },
|
||||||
|
{ key: 'textLength', type: 'integer' },
|
||||||
|
{ key: 'mode', type: 'string' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
const authentication = require('./authentication');
|
const authentication = require('./authentication');
|
||||||
const addNumbers = require('./creates/add_numbers');
|
const addNumbers = require('./creates/add_numbers');
|
||||||
const echo = require('./creates/echo');
|
const echo = require('./creates/echo');
|
||||||
|
const sha256Hash = require('./creates/sha256');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
version: require('./package.json').version,
|
version: require('./package.json').version,
|
||||||
|
|
@ -18,5 +19,6 @@ module.exports = {
|
||||||
creates: {
|
creates: {
|
||||||
[addNumbers.key]: addNumbers,
|
[addNumbers.key]: addNumbers,
|
||||||
[echo.key]: echo,
|
[echo.key]: echo,
|
||||||
|
[sha256Hash.key]: sha256Hash,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ describe('verae-activate app', () => {
|
||||||
assert.equal(authentication.type, 'custom');
|
assert.equal(authentication.type, 'custom');
|
||||||
assert.ok(App.creates.add_numbers);
|
assert.ok(App.creates.add_numbers);
|
||||||
assert.ok(App.creates.echo);
|
assert.ok(App.creates.echo);
|
||||||
|
assert.ok(App.creates.sha256_hash);
|
||||||
assert.equal(addNumbers.key, 'add_numbers');
|
assert.equal(addNumbers.key, 'add_numbers');
|
||||||
assert.ok(addNumbers.operation.inputFields.some((f) => f.key === 'number1'));
|
assert.ok(addNumbers.operation.inputFields.some((f) => f.key === 'number1'));
|
||||||
assert.ok(addNumbers.operation.inputFields.some((f) => f.key === 'number2'));
|
assert.ok(addNumbers.operation.inputFields.some((f) => f.key === 'number2'));
|
||||||
|
|
@ -64,6 +65,13 @@ describe('verae-activate app', () => {
|
||||||
assert.equal(result.sum, 5);
|
assert.equal(result.sum, 5);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sha256 hashes hello', async () => {
|
||||||
|
const sha = require('../creates/sha256');
|
||||||
|
const out = await sha.operation.perform({}, { inputData: { text: 'hello' }, authData: {} });
|
||||||
|
assert.equal(out.sha256, '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824');
|
||||||
|
assert.equal(out.mode, 'local');
|
||||||
|
});
|
||||||
|
|
||||||
it('echo returns text length', async () => {
|
it('echo returns text length', async () => {
|
||||||
const echo = require('../creates/echo');
|
const echo = require('../creates/echo');
|
||||||
const out = await echo.operation.perform({}, { inputData: { text: 'ab' }, authData: {} });
|
const out = await echo.operation.perform({}, { inputData: { text: 'ab' }, authData: {} });
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ export function createApp(options = {}) {
|
||||||
const app = express();
|
const app = express();
|
||||||
app.disable('x-powered-by');
|
app.disable('x-powered-by');
|
||||||
|
|
||||||
app.use(express.json({ limit: '1mb' }));
|
app.use(express.json({ limit: '12mb' }));
|
||||||
app.use(traceMiddleware);
|
app.use(traceMiddleware);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ async function mockGetStatus(jobId) {
|
||||||
result: job.result,
|
result: job.result,
|
||||||
completedAt: job.completedAt,
|
completedAt: job.completedAt,
|
||||||
metadata: job.metadata,
|
metadata: job.metadata,
|
||||||
|
privateMetadata: job.privateMetadata,
|
||||||
error: job.error,
|
error: job.error,
|
||||||
sha256: job.sha256,
|
sha256: job.sha256,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
64
packages/verae-zapier-middleware/src/lib/receiptPdf.js
Normal file
64
packages/verae-zapier-middleware/src/lib/receiptPdf.js
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
* Minimal PDF builder for retrieval receipts (no native deps).
|
||||||
|
* @module lib/receiptPdf
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function pdfEscape(s) {
|
||||||
|
return String(s).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} receipt
|
||||||
|
* @returns {Buffer}
|
||||||
|
*/
|
||||||
|
export function buildReceiptPdf(receipt) {
|
||||||
|
const lines = [
|
||||||
|
'Verae Time — certified retrieval receipt',
|
||||||
|
`Type: ${receipt.type}`,
|
||||||
|
`Job ID: ${receipt.jobId}`,
|
||||||
|
`SHA256: ${receipt.sha256 ?? ''}`,
|
||||||
|
`Original timestamp: ${receipt.timestamp ?? ''}`,
|
||||||
|
`Retrieved at: ${receipt.extraSeal?.retrievedAt ?? ''}`,
|
||||||
|
`Tenant: ${receipt.extraSeal?.tenantId ?? ''}`,
|
||||||
|
`Seal event: ${receipt.extraSeal?.event ?? ''}`,
|
||||||
|
`Certificate: ${String(receipt.certificate ?? '').slice(0, 80)}`,
|
||||||
|
];
|
||||||
|
|
||||||
|
const commands = lines
|
||||||
|
.map((line, i) => {
|
||||||
|
const y = 720 - i * 18;
|
||||||
|
return `BT /F1 11 Tf 50 ${y} Td (${pdfEscape(line)}) Tj ET`;
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
const stream = `${commands}\n`;
|
||||||
|
const objects = [
|
||||||
|
'1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj',
|
||||||
|
'2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj',
|
||||||
|
'3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >> endobj',
|
||||||
|
`4 0 obj << /Length ${Buffer.byteLength(stream)} >> stream\n${stream}endstream endobj`,
|
||||||
|
'5 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> endobj',
|
||||||
|
];
|
||||||
|
|
||||||
|
let offset = '%PDF-1.4\n'.length;
|
||||||
|
const xref = [0];
|
||||||
|
let body = '%PDF-1.4\n';
|
||||||
|
for (const obj of objects) {
|
||||||
|
xref.push(offset);
|
||||||
|
const chunk = `${obj}\n`;
|
||||||
|
body += chunk;
|
||||||
|
offset += Buffer.byteLength(chunk);
|
||||||
|
}
|
||||||
|
const xrefStart = offset;
|
||||||
|
let xrefTable = `xref\n0 ${objects.length + 1}\n0000000000 65535 f \n`;
|
||||||
|
for (let i = 1; i < xref.length; i += 1) {
|
||||||
|
xrefTable += `${String(xref[i]).padStart(10, '0')} 00000 n \n`;
|
||||||
|
}
|
||||||
|
body += xrefTable;
|
||||||
|
body += `trailer << /Size ${objects.length + 1} /Root 1 0 R >>\nstartxref\n${xrefStart}\n%%EOF\n`;
|
||||||
|
return Buffer.from(body, 'utf8');
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ import { webhookRoutes } from './webhookRoutes.js';
|
||||||
import { publicTenantRoutes, adminTenantRoutes } from './tenantRoutes.js';
|
import { publicTenantRoutes, adminTenantRoutes } from './tenantRoutes.js';
|
||||||
import { hashRoutes } from './hashRoutes.js';
|
import { hashRoutes } from './hashRoutes.js';
|
||||||
import { receiptRoutes } from './receiptRoutes.js';
|
import { receiptRoutes } from './receiptRoutes.js';
|
||||||
|
import { objectRoutes, publicShareRoutes } from './objectRoutes.js';
|
||||||
import { authenticate } from '../middleware/authenticate.js';
|
import { authenticate } from '../middleware/authenticate.js';
|
||||||
import { rateLimit } from '../middleware/rateLimit.js';
|
import { rateLimit } from '../middleware/rateLimit.js';
|
||||||
|
|
||||||
|
|
@ -21,6 +22,7 @@ export const apiRoutes = Router();
|
||||||
apiRoutes.use('/v1/auth', authRoutes);
|
apiRoutes.use('/v1/auth', authRoutes);
|
||||||
apiRoutes.use('/v1', publicTenantRoutes);
|
apiRoutes.use('/v1', publicTenantRoutes);
|
||||||
apiRoutes.use('/v1/admin', adminTenantRoutes);
|
apiRoutes.use('/v1/admin', adminTenantRoutes);
|
||||||
|
apiRoutes.use('/v1/shares', publicShareRoutes);
|
||||||
|
|
||||||
// Protected
|
// Protected
|
||||||
const protectedRoutes = Router();
|
const protectedRoutes = Router();
|
||||||
|
|
@ -33,5 +35,6 @@ protectedRoutes.use('/status', statusRoutes);
|
||||||
protectedRoutes.use('/webhooks', webhookRoutes);
|
protectedRoutes.use('/webhooks', webhookRoutes);
|
||||||
protectedRoutes.use('/hashes', hashRoutes);
|
protectedRoutes.use('/hashes', hashRoutes);
|
||||||
protectedRoutes.use('/receipts', receiptRoutes);
|
protectedRoutes.use('/receipts', receiptRoutes);
|
||||||
|
protectedRoutes.use('/objects', objectRoutes);
|
||||||
|
|
||||||
apiRoutes.use('/v1', protectedRoutes);
|
apiRoutes.use('/v1', protectedRoutes);
|
||||||
|
|
|
||||||
90
packages/verae-zapier-middleware/src/routes/objectRoutes.js
Normal file
90
packages/verae-zapier-middleware/src/routes/objectRoutes.js
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
/**
|
||||||
|
* Encrypted LTS mock + share grants.
|
||||||
|
* @module routes/objectRoutes
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Router } from 'express';
|
||||||
|
import { asyncHandler, AppError } from '../errors.js';
|
||||||
|
import {
|
||||||
|
putBlob,
|
||||||
|
readBlobPlaintext,
|
||||||
|
createShare,
|
||||||
|
redeemShare,
|
||||||
|
} from '../store/blobs.js';
|
||||||
|
import { createTimestamp } from '../services/timestampService.js';
|
||||||
|
|
||||||
|
export const objectRoutes = Router();
|
||||||
|
|
||||||
|
objectRoutes.post(
|
||||||
|
'/',
|
||||||
|
asyncHandler(async (req, res) => {
|
||||||
|
const { filename, contentBase64, sha256, publicMetadata, privateMetadata, data } = req.body ?? {};
|
||||||
|
if (!contentBase64 && !data) {
|
||||||
|
throw new AppError('contentBase64 or data is required', { status: 400, code: 'VALIDATION_ERROR' });
|
||||||
|
}
|
||||||
|
const bytes = Buffer.from(String(contentBase64 ?? data), contentBase64 ? 'base64' : 'utf8');
|
||||||
|
const stamp = await createTimestamp(req.auth, {
|
||||||
|
data: data ?? bytes.toString('utf8').slice(0, 1024),
|
||||||
|
sha256,
|
||||||
|
publicMetadata,
|
||||||
|
privateMetadata,
|
||||||
|
});
|
||||||
|
const stored = putBlob({
|
||||||
|
tenantId: req.auth.tenantId,
|
||||||
|
sha256: stamp.sha256 ?? sha256 ?? null,
|
||||||
|
filename,
|
||||||
|
bytes,
|
||||||
|
publicMetadata,
|
||||||
|
privateMetadata,
|
||||||
|
});
|
||||||
|
res.status(201).json({ ...stored, jobId: stamp.jobId, existing: stamp.existing });
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
objectRoutes.get(
|
||||||
|
'/:id',
|
||||||
|
asyncHandler(async (req, res) => {
|
||||||
|
try {
|
||||||
|
const rec = readBlobPlaintext(req.params.id, req.auth.tenantId);
|
||||||
|
if (!rec) throw new AppError('Object not found', { status: 404, code: 'NOT_FOUND' });
|
||||||
|
res.json({
|
||||||
|
id: rec.id,
|
||||||
|
filename: rec.filename,
|
||||||
|
size: rec.size,
|
||||||
|
sha256: rec.sha256,
|
||||||
|
publicMetadata: rec.publicMetadata,
|
||||||
|
privateMetadata: rec.privateMetadata,
|
||||||
|
contentBase64: rec.bytes.toString('base64'),
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code === 'FORBIDDEN') {
|
||||||
|
throw new AppError('Forbidden', { status: 403, code: 'FORBIDDEN' });
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
objectRoutes.post(
|
||||||
|
'/:id/share',
|
||||||
|
asyncHandler(async (req, res) => {
|
||||||
|
const share = createShare({
|
||||||
|
blobId: req.params.id,
|
||||||
|
fromTenantId: req.auth.tenantId,
|
||||||
|
label: req.body?.label,
|
||||||
|
});
|
||||||
|
if (!share) throw new AppError('Object not found', { status: 404, code: 'NOT_FOUND' });
|
||||||
|
res.status(201).json(share);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const publicShareRoutes = Router();
|
||||||
|
|
||||||
|
publicShareRoutes.get(
|
||||||
|
'/:token',
|
||||||
|
asyncHandler(async (req, res) => {
|
||||||
|
const payload = redeemShare(req.params.token);
|
||||||
|
if (!payload) throw new AppError('Share not found', { status: 404, code: 'NOT_FOUND' });
|
||||||
|
res.json(payload);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import { asyncHandler, AppError } from '../errors.js';
|
import { asyncHandler, AppError } from '../errors.js';
|
||||||
import { getJobStatus } from '../services/timestampService.js';
|
import { getJobStatus } from '../services/timestampService.js';
|
||||||
|
import { buildReceiptPdf } from '../lib/receiptPdf.js';
|
||||||
|
|
||||||
export const receiptRoutes = Router();
|
export const receiptRoutes = Router();
|
||||||
|
|
||||||
|
|
@ -33,10 +34,11 @@ receiptRoutes.get(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (format === 'pdf') {
|
if (format === 'pdf') {
|
||||||
throw new AppError('PDF receipts not implemented; use format=json', {
|
const pdf = buildReceiptPdf({ ...receipt, format: 'pdf' });
|
||||||
status: 501,
|
res.setHeader('content-type', 'application/pdf');
|
||||||
code: 'NOT_IMPLEMENTED',
|
res.setHeader('content-disposition', `attachment; filename="verae-receipt-${jobId}.pdf"`);
|
||||||
});
|
res.send(pdf);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
res.json(receipt);
|
res.json(receipt);
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,11 @@ statusRoutes.get(
|
||||||
throw new AppError('jobId is required', { status: 400, code: 'VALIDATION_ERROR' });
|
throw new AppError('jobId is required', { status: 400, code: 'VALIDATION_ERROR' });
|
||||||
}
|
}
|
||||||
const result = await getJobStatus(req.auth, jobId);
|
const result = await getJobStatus(req.auth, jobId);
|
||||||
|
if (req.query.scope === 'public' && result.metadata) {
|
||||||
|
const { publicMetadata, ...restMeta } = result.metadata;
|
||||||
|
result.metadata = { ...restMeta, publicMetadata };
|
||||||
|
delete result.privateMetadata;
|
||||||
|
}
|
||||||
res.json(result);
|
res.json(result);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
136
packages/verae-zapier-middleware/src/store/blobs.js
Normal file
136
packages/verae-zapier-middleware/src/store/blobs.js
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
/**
|
||||||
|
* Mock encrypted long-term object store + share grants.
|
||||||
|
* @module store/blobs
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createCipheriv, createDecipheriv, randomBytes, randomUUID } from 'node:crypto';
|
||||||
|
import { getStore, persist } from './db.js';
|
||||||
|
|
||||||
|
const ALGO = 'aes-256-gcm';
|
||||||
|
|
||||||
|
function ensure() {
|
||||||
|
const store = getStore();
|
||||||
|
store.blobs ??= {};
|
||||||
|
store.shares ??= {};
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Buffer} plaintext
|
||||||
|
* @param {Buffer} key 32 bytes
|
||||||
|
*/
|
||||||
|
function encrypt(plaintext, key) {
|
||||||
|
const iv = randomBytes(12);
|
||||||
|
const cipher = createCipheriv(ALGO, key, iv);
|
||||||
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
||||||
|
const tag = cipher.getAuthTag();
|
||||||
|
return { iv: iv.toString('base64'), tag: tag.toString('base64'), ciphertext: ciphertext.toString('base64') };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{ iv: string, tag: string, ciphertext: string }} enc
|
||||||
|
* @param {Buffer} key
|
||||||
|
*/
|
||||||
|
export function decryptBlob(enc, key) {
|
||||||
|
const decipher = createDecipheriv(ALGO, key, Buffer.from(enc.iv, 'base64'));
|
||||||
|
decipher.setAuthTag(Buffer.from(enc.tag, 'base64'));
|
||||||
|
return Buffer.concat([
|
||||||
|
decipher.update(Buffer.from(enc.ciphertext, 'base64')),
|
||||||
|
decipher.final(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} params
|
||||||
|
* @param {string} params.tenantId
|
||||||
|
* @param {string} [params.sha256]
|
||||||
|
* @param {string} [params.filename]
|
||||||
|
* @param {Buffer} params.bytes
|
||||||
|
* @param {object} [params.publicMetadata]
|
||||||
|
* @param {object} [params.privateMetadata]
|
||||||
|
*/
|
||||||
|
export function putBlob({ tenantId, sha256, filename, bytes, publicMetadata = {}, privateMetadata = {} }) {
|
||||||
|
const store = ensure();
|
||||||
|
const id = randomUUID();
|
||||||
|
const key = randomBytes(32);
|
||||||
|
const enc = encrypt(bytes, key);
|
||||||
|
store.blobs[id] = {
|
||||||
|
id,
|
||||||
|
tenantId,
|
||||||
|
sha256: sha256 ?? null,
|
||||||
|
filename: filename ?? 'object.bin',
|
||||||
|
size: bytes.length,
|
||||||
|
enc,
|
||||||
|
dek: key.toString('base64'),
|
||||||
|
publicMetadata,
|
||||||
|
privateMetadata,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
persist();
|
||||||
|
return { id, sha256: store.blobs[id].sha256, size: bytes.length, filename: store.blobs[id].filename };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBlobRecord(id) {
|
||||||
|
return ensure().blobs[id] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt for owner tenant only.
|
||||||
|
* @param {string} id
|
||||||
|
* @param {string} tenantId
|
||||||
|
*/
|
||||||
|
export function readBlobPlaintext(id, tenantId) {
|
||||||
|
const rec = getBlobRecord(id);
|
||||||
|
if (!rec) return null;
|
||||||
|
if (rec.tenantId !== tenantId) {
|
||||||
|
const err = new Error('Forbidden');
|
||||||
|
err.code = 'FORBIDDEN';
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...rec,
|
||||||
|
bytes: decryptBlob(rec.enc, Buffer.from(rec.dek, 'base64')),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{ blobId: string, fromTenantId: string, label?: string }} params
|
||||||
|
*/
|
||||||
|
export function createShare({ blobId, fromTenantId, label }) {
|
||||||
|
const rec = getBlobRecord(blobId);
|
||||||
|
if (!rec || rec.tenantId !== fromTenantId) return null;
|
||||||
|
const token = `shr_${randomBytes(16).toString('hex')}`;
|
||||||
|
const store = ensure();
|
||||||
|
store.shares[token] = {
|
||||||
|
token,
|
||||||
|
blobId,
|
||||||
|
fromTenantId,
|
||||||
|
label: label ?? rec.filename,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
persist();
|
||||||
|
return { token, blobId, label: store.shares[token].label };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redeem a share token: ciphertext + wrapped key for the recipient.
|
||||||
|
* @param {string} token
|
||||||
|
*/
|
||||||
|
export function redeemShare(token) {
|
||||||
|
const store = ensure();
|
||||||
|
const share = store.shares[token];
|
||||||
|
if (!share) return null;
|
||||||
|
const rec = store.blobs[share.blobId];
|
||||||
|
if (!rec) return null;
|
||||||
|
return {
|
||||||
|
token,
|
||||||
|
blobId: rec.id,
|
||||||
|
filename: rec.filename,
|
||||||
|
size: rec.size,
|
||||||
|
sha256: rec.sha256,
|
||||||
|
publicMetadata: rec.publicMetadata ?? {},
|
||||||
|
enc: rec.enc,
|
||||||
|
dek: rec.dek,
|
||||||
|
note: 'Mock share: dek is included so the recipient can decrypt. Production must wrap dek to recipient pubkey.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -33,6 +33,8 @@ export function emptyStore() {
|
||||||
usage: {},
|
usage: {},
|
||||||
webhooks: [],
|
webhooks: [],
|
||||||
jobWatchers: [],
|
jobWatchers: [],
|
||||||
|
blobs: {},
|
||||||
|
shares: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
import { describe, it, before, after } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import http from 'node:http';
|
||||||
|
import { createApp } from '../../src/app.js';
|
||||||
|
import { useTempStore, seedProTenant, seedFreeTenant } from '../helpers.js';
|
||||||
|
import { clearMockJobs } from '../../src/clients/veraeClient.js';
|
||||||
|
import { config } from '../../src/config.js';
|
||||||
|
|
||||||
|
describe('encrypted objects + share + pdf receipt', () => {
|
||||||
|
let ctx;
|
||||||
|
let server;
|
||||||
|
let port;
|
||||||
|
let ownerKey;
|
||||||
|
let otherKey;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
config.mockVerae = true;
|
||||||
|
config.natsEnabled = false;
|
||||||
|
config.mockJobCompleteMs = 40;
|
||||||
|
clearMockJobs();
|
||||||
|
ctx = useTempStore();
|
||||||
|
ownerKey = seedProTenant().apiKey;
|
||||||
|
otherKey = seedFreeTenant({ id: 'tenant-other', veraeUsername: 'other', veraePassword: 'otherpass' }).apiKey;
|
||||||
|
const app = createApp();
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
server = app.listen(0, '127.0.0.1', () => {
|
||||||
|
port = server.address().port;
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await new Promise((r) => server.close(r));
|
||||||
|
ctx.cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stores encrypted blob, hides it from other tenants, shares via token', async () => {
|
||||||
|
const created = await call(port, 'POST', '/zapier/v1/objects', ownerKey, {
|
||||||
|
filename: 'secret.txt',
|
||||||
|
contentBase64: Buffer.from('top-secret').toString('base64'),
|
||||||
|
publicMetadata: { title: 'pub' },
|
||||||
|
privateMetadata: { ssn: '000' },
|
||||||
|
});
|
||||||
|
assert.equal(created.status, 201);
|
||||||
|
const id = created.body.id;
|
||||||
|
|
||||||
|
const mine = await call(port, 'GET', `/zapier/v1/objects/${id}`, ownerKey);
|
||||||
|
assert.equal(mine.status, 200);
|
||||||
|
assert.equal(Buffer.from(mine.body.contentBase64, 'base64').toString(), 'top-secret');
|
||||||
|
assert.equal(mine.body.privateMetadata.ssn, '000');
|
||||||
|
|
||||||
|
const theirs = await call(port, 'GET', `/zapier/v1/objects/${id}`, otherKey);
|
||||||
|
assert.equal(theirs.status, 403);
|
||||||
|
|
||||||
|
const share = await call(port, 'POST', `/zapier/v1/objects/${id}/share`, ownerKey, { label: 'for-other' });
|
||||||
|
assert.equal(share.status, 201);
|
||||||
|
const redeemed = await call(port, 'GET', `/zapier/v1/shares/${share.body.token}`, otherKey);
|
||||||
|
assert.equal(redeemed.status, 200);
|
||||||
|
assert.equal(redeemed.body.filename, 'secret.txt');
|
||||||
|
assert.ok(redeemed.body.dek);
|
||||||
|
assert.equal(redeemed.body.publicMetadata.title, 'pub');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a PDF receipt starting with %PDF', async () => {
|
||||||
|
const wait = await call(port, 'POST', '/zapier/v1/timestamp/wait', ownerKey, { data: 'pdf-receipt' });
|
||||||
|
const jobId = wait.body.id;
|
||||||
|
const rec = await raw(port, 'GET', `/zapier/v1/receipts/${jobId}?format=pdf`, ownerKey);
|
||||||
|
assert.equal(rec.status, 200);
|
||||||
|
assert.match(rec.body.toString('latin1').slice(0, 5), /%PDF-/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function call(port, method, path, apiKey, body) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = http.request(
|
||||||
|
{
|
||||||
|
hostname: '127.0.0.1',
|
||||||
|
port,
|
||||||
|
path,
|
||||||
|
method,
|
||||||
|
headers: { authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' },
|
||||||
|
},
|
||||||
|
(res) => {
|
||||||
|
const chunks = [];
|
||||||
|
res.on('data', (c) => chunks.push(c));
|
||||||
|
res.on('end', () => {
|
||||||
|
const buf = Buffer.concat(chunks);
|
||||||
|
let parsed = {};
|
||||||
|
try {
|
||||||
|
parsed = buf.length ? JSON.parse(buf.toString()) : {};
|
||||||
|
} catch {
|
||||||
|
parsed = { raw: buf.toString() };
|
||||||
|
}
|
||||||
|
resolve({ status: res.statusCode, body: parsed });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
req.on('error', reject);
|
||||||
|
if (body) req.write(JSON.stringify(body));
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function raw(port, method, path, apiKey) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = http.request(
|
||||||
|
{
|
||||||
|
hostname: '127.0.0.1',
|
||||||
|
port,
|
||||||
|
path,
|
||||||
|
method,
|
||||||
|
headers: { authorization: `Bearer ${apiKey}` },
|
||||||
|
},
|
||||||
|
(res) => {
|
||||||
|
const chunks = [];
|
||||||
|
res.on('data', (c) => chunks.push(c));
|
||||||
|
res.on('end', () => resolve({ status: res.statusCode, body: Buffer.concat(chunks) }));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
req.on('error', reject);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
16
scripts/nats-tunnel.sh
Executable file
16
scripts/nats-tunnel.sh
Executable file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# SSH tunnel NS1 JetStream (127.0.0.1:4222 on 70.88.205.138) to local 14222.
|
||||||
|
set -euo pipefail
|
||||||
|
HOST="${NATS_SSH_HOST:-marchon@70.88.205.138}"
|
||||||
|
LOCAL_PORT="${NATS_LOCAL_PORT:-14222}"
|
||||||
|
REMOTE="127.0.0.1:4222"
|
||||||
|
|
||||||
|
if nc -z 127.0.0.1 "$LOCAL_PORT" 2>/dev/null; then
|
||||||
|
echo "already listening on 127.0.0.1:${LOCAL_PORT}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ssh -fN -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 \
|
||||||
|
-L "${LOCAL_PORT}:${REMOTE}" "$HOST"
|
||||||
|
echo "NATS_URL=nats://127.0.0.1:${LOCAL_PORT}"
|
||||||
|
echo "export NATS_URL=nats://127.0.0.1:${LOCAL_PORT} NATS_ENABLED=true"
|
||||||
21
scripts/test-offline.sh
Executable file
21
scripts/test-offline.sh
Executable file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Tests that do not need Zapier login or live api.veraetime.net.
|
||||||
|
set -euo pipefail
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
cd "$ROOT"
|
||||||
|
export MOCK_VERAE=true
|
||||||
|
npm run gate:0
|
||||||
|
npm run gate:1
|
||||||
|
npm run gate:2
|
||||||
|
npm run gate:3
|
||||||
|
npm run gate:4
|
||||||
|
npm run gate:5
|
||||||
|
npm run gate:6
|
||||||
|
npm run gate:10
|
||||||
|
npm run gate:11
|
||||||
|
npm --prefix packages/verae-zapier-middleware run test:hash
|
||||||
|
node --test packages/verae-zapier-middleware/test/integration/receipt.test.js
|
||||||
|
node --test packages/verae-zapier-middleware/test/integration/objects.test.js
|
||||||
|
npm run test:activate
|
||||||
|
npm run test:zappier
|
||||||
|
echo "OFFLINE SUITE PASSED"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue