first commit
This commit is contained in:
30
README.md
Normal file
30
README.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# @packages/mailing
|
||||||
|
|
||||||
|
Client réutilisable pour l'envoi d'emails via Postal.
|
||||||
|
|
||||||
|
## Export
|
||||||
|
|
||||||
|
- `@packages/mailing/server`
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
- `createPostalClient`
|
||||||
|
- `createPostalClientFromEnv`
|
||||||
|
|
||||||
|
## Variables d'environnement par défaut
|
||||||
|
|
||||||
|
- `POSTAL_URL`
|
||||||
|
- `POSTAL_API_KEY`
|
||||||
|
- `POSTAL_MESSAGE_FROM`
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build --workspace @packages/mailing
|
||||||
|
```
|
||||||
|
|
||||||
|
## Watch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev --workspace @packages/mailing
|
||||||
|
```
|
||||||
27
dist/server/index.d.ts
vendored
Normal file
27
dist/server/index.d.ts
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
type PostalMessageInput = {
|
||||||
|
to: string[];
|
||||||
|
subject: string;
|
||||||
|
plainBody: string;
|
||||||
|
htmlBody: string;
|
||||||
|
};
|
||||||
|
type PostalClient = {
|
||||||
|
enabled: boolean;
|
||||||
|
sendMessage: (input: PostalMessageInput) => Promise<void>;
|
||||||
|
};
|
||||||
|
type CreatePostalClientOptions = {
|
||||||
|
baseUrl: string;
|
||||||
|
apiKey: string;
|
||||||
|
from: string;
|
||||||
|
fetchImpl?: typeof fetch;
|
||||||
|
};
|
||||||
|
type CreatePostalClientFromEnvOptions = {
|
||||||
|
baseUrlEnv?: string;
|
||||||
|
apiKeyEnv?: string;
|
||||||
|
fromEnv?: string;
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
|
fetchImpl?: typeof fetch;
|
||||||
|
};
|
||||||
|
declare function createPostalClient(options: CreatePostalClientOptions): PostalClient;
|
||||||
|
declare function createPostalClientFromEnv(options?: CreatePostalClientFromEnvOptions): PostalClient;
|
||||||
|
|
||||||
|
export { type CreatePostalClientOptions, type PostalClient, type PostalMessageInput, createPostalClient, createPostalClientFromEnv };
|
||||||
52
dist/server/index.js
vendored
Normal file
52
dist/server/index.js
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// server/postal.ts
|
||||||
|
function createPostalClient(options) {
|
||||||
|
const baseUrl = options.baseUrl.trim();
|
||||||
|
const apiKey = options.apiKey.trim();
|
||||||
|
const from = options.from.trim();
|
||||||
|
const fetchImpl = options.fetchImpl ?? fetch;
|
||||||
|
const enabled = baseUrl.length > 0 && apiKey.length > 0 && from.length > 0;
|
||||||
|
return {
|
||||||
|
enabled,
|
||||||
|
async sendMessage(input) {
|
||||||
|
if (!enabled || input.to.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const endpoint = new URL("/api/v1/send/message", ensureTrailingSlash(baseUrl));
|
||||||
|
const response = await fetchImpl(endpoint, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Server-API-Key": apiKey
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
from,
|
||||||
|
to: input.to,
|
||||||
|
subject: input.subject,
|
||||||
|
plain_body: input.plainBody,
|
||||||
|
html_body: input.htmlBody
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const payload = await response.json().catch(() => null);
|
||||||
|
if (!response.ok || payload?.status !== "success") {
|
||||||
|
throw new Error(`postal_send_failed:${response.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function createPostalClientFromEnv(options = {}) {
|
||||||
|
const env = options.env ?? process.env;
|
||||||
|
return createPostalClient({
|
||||||
|
baseUrl: env[options.baseUrlEnv ?? "POSTAL_URL"] ?? "",
|
||||||
|
apiKey: env[options.apiKeyEnv ?? "POSTAL_API_KEY"] ?? "",
|
||||||
|
from: env[options.fromEnv ?? "POSTAL_MESSAGE_FROM"] ?? "",
|
||||||
|
fetchImpl: options.fetchImpl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function ensureTrailingSlash(value) {
|
||||||
|
return value.endsWith("/") ? value : `${value}/`;
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
createPostalClient,
|
||||||
|
createPostalClientFromEnv
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
1
dist/server/index.js.map
vendored
Normal file
1
dist/server/index.js.map
vendored
Normal file
@@ -0,0 +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":[]}
|
||||||
19
package.json
Normal file
19
package.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "@packages/mailing",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"exports": {
|
||||||
|
"./server": {
|
||||||
|
"types": "./dist/server/index.d.ts",
|
||||||
|
"default": "./dist/server/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsup --config tsup.config.ts",
|
||||||
|
"dev": "tsup --config tsup.config.ts --watch"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
server/index.ts
Normal file
2
server/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { createPostalClient, createPostalClientFromEnv } from "./postal.js";
|
||||||
|
export type { CreatePostalClientOptions, PostalClient, PostalMessageInput } from "./postal.js";
|
||||||
79
server/postal.ts
Normal file
79
server/postal.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
export type PostalMessageInput = {
|
||||||
|
to: string[];
|
||||||
|
subject: string;
|
||||||
|
plainBody: string;
|
||||||
|
htmlBody: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PostalClient = {
|
||||||
|
enabled: boolean;
|
||||||
|
sendMessage: (input: PostalMessageInput) => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreatePostalClientOptions = {
|
||||||
|
baseUrl: string;
|
||||||
|
apiKey: string;
|
||||||
|
from: string;
|
||||||
|
fetchImpl?: typeof fetch;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreatePostalClientFromEnvOptions = {
|
||||||
|
baseUrlEnv?: string;
|
||||||
|
apiKeyEnv?: string;
|
||||||
|
fromEnv?: string;
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
|
fetchImpl?: typeof fetch;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createPostalClient(options: CreatePostalClientOptions): PostalClient {
|
||||||
|
const baseUrl = options.baseUrl.trim();
|
||||||
|
const apiKey = options.apiKey.trim();
|
||||||
|
const from = options.from.trim();
|
||||||
|
const fetchImpl = options.fetchImpl ?? fetch;
|
||||||
|
const enabled = baseUrl.length > 0 && apiKey.length > 0 && from.length > 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
enabled,
|
||||||
|
async sendMessage(input: PostalMessageInput): Promise<void> {
|
||||||
|
if (!enabled || input.to.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const endpoint = new URL("/api/v1/send/message", ensureTrailingSlash(baseUrl));
|
||||||
|
const response = await fetchImpl(endpoint, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Server-API-Key": apiKey
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
from,
|
||||||
|
to: input.to,
|
||||||
|
subject: input.subject,
|
||||||
|
plain_body: input.plainBody,
|
||||||
|
html_body: input.htmlBody
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const payload = (await response.json().catch(() => null)) as { status?: string } | null;
|
||||||
|
if (!response.ok || payload?.status !== "success") {
|
||||||
|
throw new Error(`postal_send_failed:${response.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPostalClientFromEnv(options: CreatePostalClientFromEnvOptions = {}): PostalClient {
|
||||||
|
const env = options.env ?? process.env;
|
||||||
|
|
||||||
|
return createPostalClient({
|
||||||
|
baseUrl: env[options.baseUrlEnv ?? "POSTAL_URL"] ?? "",
|
||||||
|
apiKey: env[options.apiKeyEnv ?? "POSTAL_API_KEY"] ?? "",
|
||||||
|
from: env[options.fromEnv ?? "POSTAL_MESSAGE_FROM"] ?? "",
|
||||||
|
fetchImpl: options.fetchImpl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureTrailingSlash(value: string): string {
|
||||||
|
return value.endsWith("/") ? value : `${value}/`;
|
||||||
|
}
|
||||||
13
tsconfig.json
Normal file
13
tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"strict": true,
|
||||||
|
"declaration": true,
|
||||||
|
"emitDeclarationOnly": false,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["server"]
|
||||||
|
}
|
||||||
13
tsup.config.ts
Normal file
13
tsup.config.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { defineConfig } from "tsup";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
tsconfig: "tsconfig.json",
|
||||||
|
entry: {
|
||||||
|
"server/index": "server/index.ts"
|
||||||
|
},
|
||||||
|
format: ["esm"],
|
||||||
|
dts: true,
|
||||||
|
sourcemap: true,
|
||||||
|
clean: true,
|
||||||
|
outDir: "dist"
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user