Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1baffe2
cnc jobs up and combined server
Zetazzz Jan 7, 2026
6230fbc
E2E Test cloud fns
Zetazzz Jan 8, 2026
cf29d75
Merge branch 'main' into feat/cloud-fn-test
Zetazzz Jan 8, 2026
6fe4a31
refactor test on combined server
Zetazzz Jan 8, 2026
14cc9ee
jobs combined server tested in CI
Zetazzz Jan 8, 2026
9809afd
Merge branch 'main' into feat/cloud-fn-test
Zetazzz Jan 8, 2026
1ad6dde
refine jobs e2e test
Zetazzz Jan 8, 2026
975191a
Merge branch 'main' into feat/cloud-fn-test
Zetazzz Jan 8, 2026
e77b4ae
Merge branch 'main' into feat/cloud-fn-test
Zetazzz Jan 8, 2026
309bc31
use default job callback port 12345
Zetazzz Jan 9, 2026
9e5c440
added smtppostmaster
Zetazzz Jan 15, 2026
f9af447
switch of smtp email in cloud functions
Zetazzz Jan 15, 2026
f1c3445
Merge branch 'main' into feat/cloud-fn-test
Zetazzz Jan 15, 2026
c0d1b48
fixed logger printing objs format
Zetazzz Jan 15, 2026
601b885
add comments
Zetazzz Jan 15, 2026
b5b6031
update test comments
Zetazzz Jan 15, 2026
67124f1
apply loggers
Zetazzz Jan 15, 2026
d9d4a6c
fix jobs e2e test and update README
Zetazzz Jan 16, 2026
759e783
jobs e2e test added failure cases
Zetazzz Jan 18, 2026
4250163
move combined server to knative job svc
Zetazzz Jan 18, 2026
808fc47
Merge branch 'main' into feat/cloud-fn-test
Zetazzz Jan 18, 2026
f3de8d1
remove packages/server
Zetazzz Jan 18, 2026
6302097
add knative job svc test to e2e test matrix
Zetazzz Jan 18, 2026
88c78d6
refactor(smtppostmaster): use centralized env system for SMTP config
pyramation Jan 18, 2026
bb1dcc2
test(env): update snapshot to include smtp defaults
pyramation Jan 18, 2026
ada5524
Merge pull request #619 from constructive-io/devin/1768714819-smtp-en…
pyramation Jan 18, 2026
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: jobs/knative-job-service
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