Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/run-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ jobs:
env: {}
- package: packages/cli
env: {}
- package: packages/server
env: {}
- package: packages/client
env:
TEST_DATABASE_URL: postgres://postgres:password@localhost:5432/postgres
Expand Down
13 changes: 11 additions & 2 deletions functions/send-email-link/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,22 @@ Recommended / optional:
- `LOCAL_APP_PORT`
Optional port suffix for localhost-style hosts (e.g. `3000`). When the resolved hostname is `localhost` / `*.localhost` and `SEND_EMAIL_LINK_DRY_RUN=true`, links are generated as `http://localhost:LOCAL_APP_PORT/...`. Ignored for non-local hostnames and in production.

Email delivery (used by `@launchql/postmaster`):
Email delivery (default: `@launchql/postmaster`):

- Typically Mailgun or another provider; consult `@launchql/postmaster` docs. A common pattern is:
- Set `EMAIL_SEND_USE_SMTP=true` to switch to `@constructive-io/smtppostmaster` (SMTP). Otherwise it uses `@launchql/postmaster`.

- Mailgun or another provider; consult `@launchql/postmaster` docs. A common pattern is:
- `MAILGUN_API_KEY`
- `MAILGUN_DOMAIN`
- `MAILGUN_FROM`

- SMTP variables when `EMAIL_SEND_USE_SMTP=true`:
- `SMTP_HOST`
- `SMTP_PORT`
- `SMTP_USER`
- `SMTP_PASS`
- `SMTP_FROM`

## Building locally

From the repo root:
Expand Down
2 changes: 2 additions & 0 deletions functions/send-email-link/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
},
"dependencies": {
"@constructive-io/knative-job-fn": "workspace:^",
"@constructive-io/smtppostmaster": "workspace:^",
"@launchql/mjml": "0.1.1",
"@launchql/postmaster": "0.1.4",
"@launchql/styled-email": "0.1.0",
"@pgpmjs/env": "workspace:^",
"@pgpmjs/logger": "workspace:^",
"graphql-request": "^7.1.2",
"graphql-tag": "^2.12.6"
}
Expand Down
18 changes: 11 additions & 7 deletions functions/send-email-link/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import app from '@constructive-io/knative-job-fn';
import { createJobApp } from '@constructive-io/knative-job-fn';
import { GraphQLClient } from 'graphql-request';
import gql from 'graphql-tag';
import { generate } from '@launchql/mjml';
import { send } from '@launchql/postmaster';
import { send as sendPostmaster } from '@launchql/postmaster';
import { send as sendSmtp } from '@constructive-io/smtppostmaster';
import { parseEnvBoolean } from '@pgpmjs/env';
import { createLogger } from '@pgpmjs/logger';

const isDryRun = parseEnvBoolean(process.env.SEND_EMAIL_LINK_DRY_RUN) ?? false;
const useSmtp = parseEnvBoolean(process.env.EMAIL_SEND_USE_SMTP) ?? false;
const logger = createLogger('send-email-link');
const app = createJobApp();

const GetUser = gql`
query GetUser($userId: UUID!) {
Expand Down Expand Up @@ -273,15 +278,15 @@ export const sendEmailLink = async (
});

if (isDryRun) {
// eslint-disable-next-line no-console
console.log('[send-email-link] DRY RUN email (skipping send)', {
logger.info('DRY RUN email (skipping send)', {
email_type: params.email_type,
email: params.email,
subject,
link
});
} else {
await send({
const sendEmail = useSmtp ? sendSmtp : sendPostmaster;
await sendEmail({
to: params.email,
subject,
html
Expand Down Expand Up @@ -330,7 +335,6 @@ if (require.main === module) {
const port = Number(process.env.PORT ?? 8080);
// @constructive-io/knative-job-fn exposes a .listen method that delegates to the Express app
(app as any).listen(port, () => {
// eslint-disable-next-line no-console
console.log(`[send-email-link] listening on port ${port}`);
logger.info(`listening on port ${port}`);
});
}
30 changes: 23 additions & 7 deletions functions/simple-email/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
Simple Knative-compatible email function used with the Constructive jobs system.

This function is intentionally minimal: it reads an email payload from the job
body and **logs it only** (dry‑run mode). It does **not** send any email. This
is useful while wiring up jobs and Knative without needing a real mail
provider configured.
body and **logs it only** in dry‑run mode. When not in dry‑run, it sends via
the configured email provider. This is useful while wiring up jobs and Knative
without needing a real mail provider configured.

## Expected job payload

Expand Down Expand Up @@ -62,10 +62,26 @@ callback for the worker.

## Environment variables

This function does **not** depend on any GraphQL or email provider
configuration. There are currently **no required environment variables** for
its core behavior; it will simply log the email payload and return a successful
response.
Email provider configuration is only required when not running in dry‑run mode.

Optional:

- `SIMPLE_EMAIL_DRY_RUN` (`true`/`false`): log only, skip send.
- `EMAIL_SEND_USE_SMTP` (`true`/`false`): use SMTP (`@constructive-io/smtppostmaster`).

Mailgun (`@launchql/postmaster`) env vars when `EMAIL_SEND_USE_SMTP` is false:

- `MAILGUN_API_KEY`
- `MAILGUN_DOMAIN`
- `MAILGUN_FROM`

SMTP env vars when `EMAIL_SEND_USE_SMTP` is true:

- `SMTP_HOST`
- `SMTP_PORT`
- `SMTP_USER`
- `SMTP_PASS`
- `SMTP_FROM`

## Building locally

Expand Down
4 changes: 3 additions & 1 deletion functions/simple-email/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
},
"dependencies": {
"@constructive-io/knative-job-fn": "workspace:^",
"@constructive-io/smtppostmaster": "workspace:^",
"@launchql/postmaster": "0.1.4",
"@pgpmjs/env": "workspace:^"
"@pgpmjs/env": "workspace:^",
"@pgpmjs/logger": "workspace:^"
}
}
21 changes: 12 additions & 9 deletions functions/simple-email/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import app from '@constructive-io/knative-job-fn';
import { createJobApp } from '@constructive-io/knative-job-fn';
import { send as sendSmtp } from '@constructive-io/smtppostmaster';
import { send as sendPostmaster } from '@launchql/postmaster';
import { parseEnvBoolean } from '@pgpmjs/env';
import { send as sendEmail } from '@launchql/postmaster';
import { createLogger } from '@pgpmjs/logger';

type SimpleEmailPayload = {
to: string;
Expand All @@ -26,6 +28,9 @@ const getRequiredField = (
};

const isDryRun = parseEnvBoolean(process.env.SIMPLE_EMAIL_DRY_RUN) ?? false;
const useSmtp = parseEnvBoolean(process.env.EMAIL_SEND_USE_SMTP) ?? false;
const logger = createLogger('simple-email');
const app = createJobApp();

app.post('/', async (req: any, res: any, next: any) => {
try {
Expand All @@ -41,7 +46,7 @@ app.post('/', async (req: any, res: any, next: any) => {
throw new Error("Either 'html' or 'text' must be provided");
}

const fromEnv = process.env.MAILGUN_FROM;
const fromEnv = useSmtp ? process.env.SMTP_FROM : process.env.MAILGUN_FROM;
const from = isNonEmptyString(payload.from)
? payload.from
: isNonEmptyString(fromEnv)
Expand All @@ -62,10 +67,10 @@ app.post('/', async (req: any, res: any, next: any) => {
};

if (isDryRun) {
// eslint-disable-next-line no-console
console.log('[simple-email] DRY RUN email (no send)', logContext);
logger.info('DRY RUN email (no send)', logContext);
} else {
// Send via the Postmaster package (Mailgun or configured provider)
const sendEmail = useSmtp ? sendSmtp : sendPostmaster;
await sendEmail({
to,
subject,
Expand All @@ -75,8 +80,7 @@ app.post('/', async (req: any, res: any, next: any) => {
...(replyTo && { replyTo })
});

// eslint-disable-next-line no-console
console.log('[simple-email] Sent email', logContext);
logger.info('Sent email', logContext);
}

res.status(200).json({ complete: true });
Expand All @@ -93,7 +97,6 @@ if (require.main === module) {
const port = Number(process.env.PORT ?? 8080);
// @constructive-io/knative-job-fn exposes a .listen method that delegates to the underlying Express app
(app as any).listen(port, () => {
// eslint-disable-next-line no-console
console.log(`[simple-email] listening on port ${port}`);
logger.info(`listening on port ${port}`);
});
}
Loading
Loading