← back to Dw Signup Fulfillment
fix George Basic-auth resolution (real 401 root cause: wrong cred source→empty pass); verify via config; abort honor live-run if Shopify token unset
d5c8741f3658dbdf4229c01a2536f19aed46af11 · 2026-08-14 11:30:39 -0700 · steve
Files touched
M lib/config.jsM scripts/george-verify.jsM scripts/honor-reissue.js
Diff
commit d5c8741f3658dbdf4229c01a2536f19aed46af11
Author: steve <steve@designerwallcoverings.com>
Date: Fri Aug 14 11:30:39 2026 -0700
fix George Basic-auth resolution (real 401 root cause: wrong cred source→empty pass); verify via config; abort honor live-run if Shopify token unset
---
lib/config.js | 12 +++++++++++-
scripts/george-verify.js | 18 ++++++++----------
scripts/honor-reissue.js | 8 ++++++++
3 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/lib/config.js b/lib/config.js
index 4a7ddc3..872e177 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -94,7 +94,17 @@ const config = {
GEORGE_ACCOUNT: process.env.GEORGE_ACCOUNT || 'steve-office',
GEORGE_FROM: process.env.GEORGE_FROM || 'info@designerwallcoverings.com',
GEORGE_EXTERNAL_SEND_TOKEN: firstEnv('GEORGE_EXTERNAL_SEND_TOKEN', GEORGE_ENVS),
- GEORGE_BASIC_AUTH: firstEnv('GEORGE_BASIC_AUTH', GEORGE_ENVS) || 'admin:',
+ // Basic-auth credential for George. Resolve from the SAME source the working
+ // token-bridge uses (secrets-manager GEORGE_AUTH) if george-gmail/.env doesn't carry
+ // GEORGE_BASIC_AUTH, then normalize any form (a "Basic <b64>" header, a bare password,
+ // or "user:pass") down to "user:pass" so email.js can base64-encode it correctly. The
+ // old 'admin:' default silently produced a 401 — this is the fix for that.
+ GEORGE_BASIC_AUTH: (() => {
+ let v = firstEnv('GEORGE_BASIC_AUTH', GEORGE_ENVS) || firstEnv('GEORGE_AUTH', SECRETS_ENVS) || 'admin:';
+ if (v.startsWith('Basic ')) { try { v = Buffer.from(v.slice(6), 'base64').toString(); } catch (e) {} }
+ if (!v.includes(':')) v = 'admin:' + v;
+ return v;
+ })(),
// Admin review surface basic-auth. Resolve order: explicit env → on-host secrets
// master → the fleet default. Reading from the secrets file lets a strong per-host
diff --git a/scripts/george-verify.js b/scripts/george-verify.js
index 2785dd0..cd85864 100644
--- a/scripts/george-verify.js
+++ b/scripts/george-verify.js
@@ -8,21 +8,19 @@
// Expected: HTTP 200 and no "blocked" field → token path healthy.
// HTTP 403 blocked → token mismatch (would need a fix).
// Run: node ~/Projects/dw-signup-fulfillment/scripts/george-verify.js
-const fs = require('fs');
-const os = require('os');
-const path = require('path');
+// Use the project's own config so this test exercises the EXACT credential path the real
+// sends use (config.GEORGE_BASIC_AUTH now resolves from secrets-manager GEORGE_AUTH and
+// normalizes to user:pass; config.GEORGE_EXTERNAL_SEND_TOKEN is the static approval token).
+const config = require('../lib/config');
-const envPath = path.join(os.homedir(), 'Projects', 'george-gmail', '.env');
-const env = fs.readFileSync(envPath, 'utf8');
-const pick = (k) => { const m = env.match(new RegExp('^' + k + '=(.+)$', 'm')); return m ? m[1].trim().replace(/^["']|["']$/g, '') : ''; };
-
-const token = pick('GEORGE_EXTERNAL_SEND_TOKEN');
-let auth = pick('GEORGE_BASIC_AUTH') || 'admin:';
+const token = config.GEORGE_EXTERNAL_SEND_TOKEN;
+const auth = config.GEORGE_BASIC_AUTH || 'admin:';
const basic = auth.startsWith('Basic ') ? auth : 'Basic ' + Buffer.from(auth.includes(':') ? auth : 'admin:' + auth).toString('base64');
console.log('token:', token ? 'SET (last4 ...' + token.slice(-4) + ')' : 'EMPTY');
+console.log('basic-auth user:', auth.split(':')[0], '(pass-len ' + ((auth.split(':')[1] || '').length) + ')');
-fetch('http://127.0.0.1:9850/api/send', {
+fetch((config.GEORGE_URL || 'http://127.0.0.1:9850') + '/api/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: basic, 'X-Send-Approval': token },
body: JSON.stringify({
diff --git a/scripts/honor-reissue.js b/scripts/honor-reissue.js
index 3314523..300fed7 100644
--- a/scripts/honor-reissue.js
+++ b/scripts/honor-reissue.js
@@ -77,6 +77,14 @@ function appendLedger(row) {
}
async function main() {
+ // SAFETY PREFLIGHT: live mode with no Shopify token would make createGiftCard/disable
+ // silently no-op (returning a synthetic stub) while STILL emailing the customer a code —
+ // i.e. a code for a gift card that doesn't exist. Refuse to run live without the token.
+ if (!config.DRY_RUN && !config.SHOPIFY_FULFILLMENT_TOKEN) {
+ console.error('ABORT: --apply requested but SHOPIFY_FULFILLMENT_TOKEN is unset. Refusing to email codes for cards that would not actually be minted. Set the token, then re-run.');
+ process.exit(2);
+ }
+
const rows = fs.readFileSync(WORKLIST, 'utf8').split('\n').filter((l) => l.trim()).map((l) => JSON.parse(l));
const done = loadLedger();
const pending = rows.filter((r) => { const d = done.get(String(r.customer_id)) || {}; return !d.minted && !d.finalized; });
← 3658732 honor-reissue: explicit --apply flag to go live (clean comma
·
back to Dw Signup Fulfillment
·
honor-reissue: 429-aware retry + gentle pacing for the 51-ca 5ea0034 →