@reddoorla/maintenance 0.10.0 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -1
- package/dist/cli/bin.js +33 -8
- package/dist/cli/bin.js.map +1 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +33 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -314,7 +314,60 @@ RESEND_WEBHOOK_SECRET=whsec_XXXX # only for the deployed webhook
|
|
|
314
314
|
|
|
315
315
|
Renders + sends every Reports row with `Draft ready=true && Approved to send=true && Sent at IS NULL`. Stamps `Sent at` + `Delivery status=pending` on each.
|
|
316
316
|
|
|
317
|
-
5. **Delivery status updates automatically** via the Resend webhook (Netlify Function at `netlify/functions/resend-webhook.mts`) — `Delivery status` flips to `delivered` / `bounced` / `complained` as events arrive.
|
|
317
|
+
5. **Delivery status updates automatically** via the Resend webhook (Netlify Function at `netlify/functions/resend-webhook.mts`) — `Delivery status` flips to `delivered` / `bounced` / `complained` as events arrive. Deploy procedure: see [Webhook deployment](#webhook-deployment-netlify--resend) below.
|
|
318
|
+
|
|
319
|
+
### Webhook deployment (Netlify + Resend)
|
|
320
|
+
|
|
321
|
+
The Resend delivery webhook lives at `netlify/functions/resend-webhook.mts` in this repo. Deploying it is a one-time operation per environment: connect the repo to a Netlify site, set three env vars, register the deployed URL with Resend.
|
|
322
|
+
|
|
323
|
+
**1. Create the Netlify site:**
|
|
324
|
+
|
|
325
|
+
- New site → Import from Git → pick `tucksravin/reddoor-maintenance`.
|
|
326
|
+
- Branch to deploy: `main`. Build settings are read from [`netlify.toml`](netlify.toml) (no build command needed — functions-only).
|
|
327
|
+
- Site name: pick something stable (e.g. `reddoor-webhooks`) since it ends up in the public webhook URL.
|
|
328
|
+
|
|
329
|
+
**2. Set env vars** in Site settings → Environment variables:
|
|
330
|
+
|
|
331
|
+
| Variable | Value |
|
|
332
|
+
| ----------------------- | --------------------------------------------------------------------------- |
|
|
333
|
+
| `AIRTABLE_PAT` | Same PAT used by the CLI (read+write on the Websites + Reports tables) |
|
|
334
|
+
| `AIRTABLE_BASE_ID` | `appHG8nLOzULzXOER` |
|
|
335
|
+
| `RESEND_WEBHOOK_SECRET` | Generated by Resend in step 4 — paste back here after creating the endpoint |
|
|
336
|
+
|
|
337
|
+
**3. Trigger a deploy** (Deploys → Trigger deploy → Deploy site). When it goes green, curl the health endpoint:
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
curl https://<your-site>.netlify.app/.netlify/functions/resend-webhook
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Expected:
|
|
344
|
+
|
|
345
|
+
```json
|
|
346
|
+
{
|
|
347
|
+
"status": "ok",
|
|
348
|
+
"service": "reddoor-resend-webhook",
|
|
349
|
+
"env": {
|
|
350
|
+
"RESEND_WEBHOOK_SECRET": true,
|
|
351
|
+
"AIRTABLE_PAT": true,
|
|
352
|
+
"AIRTABLE_BASE_ID": true
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Any `false` means the env var didn't land — re-check step 2 and re-deploy. The health endpoint reports presence-only and never returns secret values, so the output is safe to share in a support ticket.
|
|
358
|
+
|
|
359
|
+
**4. Register the webhook in Resend:** [resend.com/webhooks](https://resend.com/webhooks) → Add Endpoint → paste the function URL from step 3 → select events `email.delivered`, `email.bounced`, `email.complained` → save. Copy the generated **Signing Secret** (`whsec_…`) into Netlify's `RESEND_WEBHOOK_SECRET` env var (step 2) and trigger another deploy so the secret takes effect.
|
|
360
|
+
|
|
361
|
+
**5. End-to-end smoke test (using ERP Industrials):**
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# from your CLI:
|
|
365
|
+
reddoor-maint report erp-industrials # drafts a Maintenance report → Airtable
|
|
366
|
+
# approve the draft in Airtable, then:
|
|
367
|
+
reddoor-maint report --send-ready
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Within a few minutes, watch the ERP Reports row's `Delivery status` flip from `pending` → `delivered` as Resend pings the webhook. Function logs in Netlify show each event with its messageId + new status. If the webhook returns 5xx, svix retries; check the Netlify function log for the error envelope.
|
|
318
371
|
|
|
319
372
|
### Frequency math
|
|
320
373
|
|
package/dist/cli/bin.js
CHANGED
|
@@ -284,12 +284,37 @@ var init_reports = __esm({
|
|
|
284
284
|
|
|
285
285
|
// src/reports/maintenance-email/assets/index.ts
|
|
286
286
|
import { readFile as readFile13 } from "fs/promises";
|
|
287
|
+
import { existsSync as existsSync2 } from "fs";
|
|
287
288
|
import { dirname as dirname2, join as join21 } from "path";
|
|
288
289
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
290
|
+
function resolveAssetsDir() {
|
|
291
|
+
if (cachedAssetsDir) return cachedAssetsDir;
|
|
292
|
+
let dir = dirname2(fileURLToPath2(import.meta.url));
|
|
293
|
+
while (true) {
|
|
294
|
+
const srcCandidate = join21(dir, "src", "reports", "maintenance-email", "assets", "check.png");
|
|
295
|
+
if (existsSync2(srcCandidate)) {
|
|
296
|
+
cachedAssetsDir = dirname2(srcCandidate);
|
|
297
|
+
return cachedAssetsDir;
|
|
298
|
+
}
|
|
299
|
+
const distCandidate = join21(dir, "dist", "reports", "maintenance-email", "assets", "check.png");
|
|
300
|
+
if (existsSync2(distCandidate)) {
|
|
301
|
+
cachedAssetsDir = dirname2(distCandidate);
|
|
302
|
+
return cachedAssetsDir;
|
|
303
|
+
}
|
|
304
|
+
const parent = dirname2(dir);
|
|
305
|
+
if (parent === dir) {
|
|
306
|
+
throw new Error(
|
|
307
|
+
`loadBundledImages: could not locate maintenance-email assets dir by walking up from ${fileURLToPath2(import.meta.url)}. Checked both src/ and dist/ layouts.`
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
dir = parent;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
289
313
|
async function loadBundledImages() {
|
|
314
|
+
const assetsDir = resolveAssetsDir();
|
|
290
315
|
const [check, blurred] = await Promise.all([
|
|
291
|
-
readFile13(join21(
|
|
292
|
-
readFile13(join21(
|
|
316
|
+
readFile13(join21(assetsDir, "check.png")),
|
|
317
|
+
readFile13(join21(assetsDir, "blurredTests.jpg"))
|
|
293
318
|
]);
|
|
294
319
|
return {
|
|
295
320
|
check: {
|
|
@@ -306,13 +331,13 @@ async function loadBundledImages() {
|
|
|
306
331
|
}
|
|
307
332
|
};
|
|
308
333
|
}
|
|
309
|
-
var
|
|
334
|
+
var CHECK_CID, BLURRED_CID, cachedAssetsDir;
|
|
310
335
|
var init_assets = __esm({
|
|
311
336
|
"src/reports/maintenance-email/assets/index.ts"() {
|
|
312
337
|
"use strict";
|
|
313
|
-
here = dirname2(fileURLToPath2(import.meta.url));
|
|
314
338
|
CHECK_CID = "rd-check-png";
|
|
315
339
|
BLURRED_CID = "rd-blurred-tests-jpg";
|
|
340
|
+
cachedAssetsDir = null;
|
|
316
341
|
}
|
|
317
342
|
});
|
|
318
343
|
|
|
@@ -2940,8 +2965,8 @@ import { fileURLToPath } from "url";
|
|
|
2940
2965
|
import { dirname, join as join18 } from "path";
|
|
2941
2966
|
function selfPackageVersion(callerImportMetaUrl) {
|
|
2942
2967
|
try {
|
|
2943
|
-
const
|
|
2944
|
-
const raw = readFileSync(join18(
|
|
2968
|
+
const here2 = dirname(fileURLToPath(callerImportMetaUrl));
|
|
2969
|
+
const raw = readFileSync(join18(here2, "..", "..", "package.json"), "utf-8");
|
|
2945
2970
|
const pkg = JSON.parse(raw);
|
|
2946
2971
|
return pkg.version ?? "0.0.0";
|
|
2947
2972
|
} catch {
|
|
@@ -3498,8 +3523,8 @@ function resolvePackageVersion(fromDir) {
|
|
|
3498
3523
|
}
|
|
3499
3524
|
|
|
3500
3525
|
// src/cli/bin.ts
|
|
3501
|
-
var
|
|
3502
|
-
var version = resolvePackageVersion(
|
|
3526
|
+
var here = dirname5(fileURLToPath3(import.meta.url));
|
|
3527
|
+
var version = resolvePackageVersion(here);
|
|
3503
3528
|
var AUDIT_DESCRIPTIONS = {
|
|
3504
3529
|
deps: "Diff site package.json against the bundled baseline version map.",
|
|
3505
3530
|
lighthouse: "Run @lhci/cli autorun using the canonical lighthouserc.",
|