Ajoute le detail des erreurs Postal

This commit is contained in:
2026-07-20 04:57:09 +02:00
parent a3a136c65d
commit 3510289bdb
5 changed files with 1562 additions and 5 deletions
+10 -2
View File
@@ -26,9 +26,17 @@ function createPostalClient(options) {
html_body: input.htmlBody html_body: input.htmlBody
}) })
}); });
const payload = await response.json().catch(() => null); const rawBody = await response.text().catch(() => "");
const payload = rawBody.length > 0 ? (() => {
try {
return JSON.parse(rawBody);
} catch {
return null;
}
})() ?? null : null;
if (!response.ok || payload?.status !== "success") { if (!response.ok || payload?.status !== "success") {
throw new Error(`postal_send_failed:${response.status}`); const details = payload?.error?.trim() || payload?.message?.trim() || rawBody.trim().slice(0, 500) || "no_response_body";
throw new Error(`postal_send_failed:${response.status}:${details}`);
} }
} }
}; };
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["../../server/postal.ts"],"sourcesContent":["export type PostalMessageInput = {\n to: string[];\n subject: string;\n plainBody: string;\n htmlBody: string;\n};\n\nexport type PostalClient = {\n enabled: boolean;\n sendMessage: (input: PostalMessageInput) => Promise<void>;\n};\n\nexport type CreatePostalClientOptions = {\n baseUrl: string;\n apiKey: string;\n from: string;\n fetchImpl?: typeof fetch;\n};\n\nexport type CreatePostalClientFromEnvOptions = {\n baseUrlEnv?: string;\n apiKeyEnv?: string;\n fromEnv?: string;\n env?: Record<string, string | undefined>;\n fetchImpl?: typeof fetch;\n};\n\nexport function createPostalClient(options: CreatePostalClientOptions): PostalClient {\n const baseUrl = options.baseUrl.trim();\n const apiKey = options.apiKey.trim();\n const from = options.from.trim();\n const fetchImpl = options.fetchImpl ?? fetch;\n const enabled = baseUrl.length > 0 && apiKey.length > 0 && from.length > 0;\n\n return {\n enabled,\n async sendMessage(input: PostalMessageInput): Promise<void> {\n if (!enabled || input.to.length === 0) {\n return;\n }\n\n const endpoint = new URL(\"/api/v1/send/message\", ensureTrailingSlash(baseUrl));\n const response = await fetchImpl(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Server-API-Key\": apiKey\n },\n body: JSON.stringify({\n from,\n to: input.to,\n subject: input.subject,\n plain_body: input.plainBody,\n html_body: input.htmlBody\n })\n });\n\n const payload = (await response.json().catch(() => null)) as { status?: string } | null;\n if (!response.ok || payload?.status !== \"success\") {\n throw new Error(`postal_send_failed:${response.status}`);\n }\n }\n };\n}\n\nexport function createPostalClientFromEnv(options: CreatePostalClientFromEnvOptions = {}): PostalClient {\n const env = options.env ?? process.env;\n\n return createPostalClient({\n baseUrl: env[options.baseUrlEnv ?? \"POSTAL_URL\"] ?? \"\",\n apiKey: env[options.apiKeyEnv ?? \"POSTAL_API_KEY\"] ?? \"\",\n from: env[options.fromEnv ?? \"POSTAL_MESSAGE_FROM\"] ?? \"\",\n fetchImpl: options.fetchImpl\n });\n}\n\nfunction ensureTrailingSlash(value: string): string {\n return value.endsWith(\"/\") ? value : `${value}/`;\n}\n"],"mappings":";AA2BO,SAAS,mBAAmB,SAAkD;AACnF,QAAM,UAAU,QAAQ,QAAQ,KAAK;AACrC,QAAM,SAAS,QAAQ,OAAO,KAAK;AACnC,QAAM,OAAO,QAAQ,KAAK,KAAK;AAC/B,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,UAAU,QAAQ,SAAS,KAAK,OAAO,SAAS,KAAK,KAAK,SAAS;AAEzE,SAAO;AAAA,IACL;AAAA,IACA,MAAM,YAAY,OAA0C;AAC1D,UAAI,CAAC,WAAW,MAAM,GAAG,WAAW,GAAG;AACrC;AAAA,MACF;AAEA,YAAM,WAAW,IAAI,IAAI,wBAAwB,oBAAoB,OAAO,CAAC;AAC7E,YAAM,WAAW,MAAM,UAAU,UAAU;AAAA,QACzC,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,oBAAoB;AAAA,QACtB;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB;AAAA,UACA,IAAI,MAAM;AAAA,UACV,SAAS,MAAM;AAAA,UACf,YAAY,MAAM;AAAA,UAClB,WAAW,MAAM;AAAA,QACnB,CAAC;AAAA,MACH,CAAC;AAED,YAAM,UAAW,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,IAAI;AACvD,UAAI,CAAC,SAAS,MAAM,SAAS,WAAW,WAAW;AACjD,cAAM,IAAI,MAAM,sBAAsB,SAAS,MAAM,EAAE;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,0BAA0B,UAA4C,CAAC,GAAiB;AACtG,QAAM,MAAM,QAAQ,OAAO,QAAQ;AAEnC,SAAO,mBAAmB;AAAA,IACxB,SAAS,IAAI,QAAQ,cAAc,YAAY,KAAK;AAAA,IACpD,QAAQ,IAAI,QAAQ,aAAa,gBAAgB,KAAK;AAAA,IACtD,MAAM,IAAI,QAAQ,WAAW,qBAAqB,KAAK;AAAA,IACvD,WAAW,QAAQ;AAAA,EACrB,CAAC;AACH;AAEA,SAAS,oBAAoB,OAAuB;AAClD,SAAO,MAAM,SAAS,GAAG,IAAI,QAAQ,GAAG,KAAK;AAC/C;","names":[]} {"version":3,"sources":["../../server/postal.ts"],"sourcesContent":["export type PostalMessageInput = {\n to: string[];\n subject: string;\n plainBody: string;\n htmlBody: string;\n};\n\nexport type PostalClient = {\n enabled: boolean;\n sendMessage: (input: PostalMessageInput) => Promise<void>;\n};\n\nexport type CreatePostalClientOptions = {\n baseUrl: string;\n apiKey: string;\n from: string;\n fetchImpl?: typeof fetch;\n};\n\nexport type CreatePostalClientFromEnvOptions = {\n baseUrlEnv?: string;\n apiKeyEnv?: string;\n fromEnv?: string;\n env?: Record<string, string | undefined>;\n fetchImpl?: typeof fetch;\n};\n\nexport function createPostalClient(options: CreatePostalClientOptions): PostalClient {\n const baseUrl = options.baseUrl.trim();\n const apiKey = options.apiKey.trim();\n const from = options.from.trim();\n const fetchImpl = options.fetchImpl ?? fetch;\n const enabled = baseUrl.length > 0 && apiKey.length > 0 && from.length > 0;\n\n return {\n enabled,\n async sendMessage(input: PostalMessageInput): Promise<void> {\n if (!enabled || input.to.length === 0) {\n return;\n }\n\n const endpoint = new URL(\"/api/v1/send/message\", ensureTrailingSlash(baseUrl));\n const response = await fetchImpl(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Server-API-Key\": apiKey\n },\n body: JSON.stringify({\n from,\n to: input.to,\n subject: input.subject,\n plain_body: input.plainBody,\n html_body: input.htmlBody\n })\n });\n\n const rawBody = await response.text().catch(() => \"\");\n const payload =\n rawBody.length > 0\n ? (((() => {\n try {\n return JSON.parse(rawBody) as { status?: string; message?: string; error?: string };\n } catch {\n return null;\n }\n })()) ?? null)\n : null;\n if (!response.ok || payload?.status !== \"success\") {\n const details =\n payload?.error?.trim() ||\n payload?.message?.trim() ||\n rawBody.trim().slice(0, 500) ||\n \"no_response_body\";\n throw new Error(`postal_send_failed:${response.status}:${details}`);\n }\n }\n };\n}\n\nexport function createPostalClientFromEnv(options: CreatePostalClientFromEnvOptions = {}): PostalClient {\n const env = options.env ?? process.env;\n\n return createPostalClient({\n baseUrl: env[options.baseUrlEnv ?? \"POSTAL_URL\"] ?? \"\",\n apiKey: env[options.apiKeyEnv ?? \"POSTAL_API_KEY\"] ?? \"\",\n from: env[options.fromEnv ?? \"POSTAL_MESSAGE_FROM\"] ?? \"\",\n fetchImpl: options.fetchImpl\n });\n}\n\nfunction ensureTrailingSlash(value: string): string {\n return value.endsWith(\"/\") ? value : `${value}/`;\n}\n"],"mappings":";AA2BO,SAAS,mBAAmB,SAAkD;AACnF,QAAM,UAAU,QAAQ,QAAQ,KAAK;AACrC,QAAM,SAAS,QAAQ,OAAO,KAAK;AACnC,QAAM,OAAO,QAAQ,KAAK,KAAK;AAC/B,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,UAAU,QAAQ,SAAS,KAAK,OAAO,SAAS,KAAK,KAAK,SAAS;AAEzE,SAAO;AAAA,IACL;AAAA,IACA,MAAM,YAAY,OAA0C;AAC1D,UAAI,CAAC,WAAW,MAAM,GAAG,WAAW,GAAG;AACrC;AAAA,MACF;AAEA,YAAM,WAAW,IAAI,IAAI,wBAAwB,oBAAoB,OAAO,CAAC;AAC7E,YAAM,WAAW,MAAM,UAAU,UAAU;AAAA,QACzC,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,oBAAoB;AAAA,QACtB;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB;AAAA,UACA,IAAI,MAAM;AAAA,UACV,SAAS,MAAM;AAAA,UACf,YAAY,MAAM;AAAA,UAClB,WAAW,MAAM;AAAA,QACnB,CAAC;AAAA,MACH,CAAC;AAED,YAAM,UAAU,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACpD,YAAM,UACJ,QAAQ,SAAS,KACV,MAAM;AACP,YAAI;AACF,iBAAO,KAAK,MAAM,OAAO;AAAA,QAC3B,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF,GAAG,KAAM,OACT;AACN,UAAI,CAAC,SAAS,MAAM,SAAS,WAAW,WAAW;AACjD,cAAM,UACJ,SAAS,OAAO,KAAK,KACrB,SAAS,SAAS,KAAK,KACvB,QAAQ,KAAK,EAAE,MAAM,GAAG,GAAG,KAC3B;AACF,cAAM,IAAI,MAAM,sBAAsB,SAAS,MAAM,IAAI,OAAO,EAAE;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,0BAA0B,UAA4C,CAAC,GAAiB;AACtG,QAAM,MAAM,QAAQ,OAAO,QAAQ;AAEnC,SAAO,mBAAmB;AAAA,IACxB,SAAS,IAAI,QAAQ,cAAc,YAAY,KAAK;AAAA,IACpD,QAAQ,IAAI,QAAQ,aAAa,gBAAgB,KAAK;AAAA,IACtD,MAAM,IAAI,QAAQ,WAAW,qBAAqB,KAAK;AAAA,IACvD,WAAW,QAAQ;AAAA,EACrB,CAAC;AACH;AAEA,SAAS,oBAAoB,OAAuB;AAClD,SAAO,MAAM,SAAS,GAAG,IAAI,QAAQ,GAAG,KAAK;AAC/C;","names":[]}
+1529
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -15,5 +15,10 @@
"scripts": { "scripts": {
"build": "tsup --config tsup.config.ts", "build": "tsup --config tsup.config.ts",
"dev": "tsup --config tsup.config.ts --watch" "dev": "tsup --config tsup.config.ts --watch"
},
"devDependencies": {
"@types/node": "^24.7.2",
"tsup": "^8.5.0",
"typescript": "^5.9.3"
} }
} }
+17 -2
View File
@@ -55,9 +55,24 @@ export function createPostalClient(options: CreatePostalClientOptions): PostalCl
}) })
}); });
const payload = (await response.json().catch(() => null)) as { status?: string } | null; const rawBody = await response.text().catch(() => "");
const payload =
rawBody.length > 0
? (((() => {
try {
return JSON.parse(rawBody) as { status?: string; message?: string; error?: string };
} catch {
return null;
}
})()) ?? null)
: null;
if (!response.ok || payload?.status !== "success") { if (!response.ok || payload?.status !== "success") {
throw new Error(`postal_send_failed:${response.status}`); const details =
payload?.error?.trim() ||
payload?.message?.trim() ||
rawBody.trim().slice(0, 500) ||
"no_response_body";
throw new Error(`postal_send_failed:${response.status}:${details}`);
} }
} }
}; };