-
Notifications
You must be signed in to change notification settings - Fork 403
Control Sandbox SMTP server based on DynamoDB config, save config on app register from Saleor data #2292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Control Sandbox SMTP server based on DynamoDB config, save config on app register from Saleor data #2292
Changes from all commits
797f7a1
b97ccd7
3a8bbfd
e117874
84aca92
79bccc0
6dd1456
a6cb154
77848c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "saleor-app-smtp": minor | ||
| --- | ||
|
|
||
| App will now parse `additional_data` received from Saleor in /register endpoint and use it to save Sandbox SMTP configuration in DynamoDB. This configuration toggles if SMTP Sandbox server should be used and if all sent email receipient should be overwritten to a single email adderss. | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -113,6 +113,46 @@ If you want to use your own database, you can implement your own APL. [Check the | |||||
|
|
||||||
| [Apps guide](https://docs.saleor.io/developer/extending/apps/overview) | ||||||
|
|
||||||
| ## Fallback SMTP (Saleor Cloud) | ||||||
|
|
||||||
| The app supports a fallback SMTP mode used by Saleor Cloud to send transactional emails out of the box, before the merchant configures their own SMTP server. | ||||||
|
|
||||||
| This feature is controlled by: | ||||||
|
|
||||||
| 1. **Fallback SMTP env vars** (`FALLBACK_SMTP_HOST`, etc.) - set by Saleor Cloud on the deployment, defining the actual SMTP server credentials | ||||||
| 2. **Per-tenant config in DynamoDB** - stores `fallbackEnabled` (boolean) and optional `fallbackRedirectEmail` (string) per installation | ||||||
|
||||||
| 2. **Per-tenant config in DynamoDB** - stores `fallbackEnabled` (boolean) and optional `fallbackRedirectEmail` (string) per installation | |
| 2. **Per-tenant config in DynamoDB** - stores `fallbackEnabled` (boolean) and optional `fallbackRedirectEmail` (string) per Saleor API URL (tenant), shared across app reinstalls |
Copilot
AI
Mar 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
README states: "If no additional_data is provided or fallbackEnabled is missing, fallback is not configured." Current implementation defaults to fallbackEnabled: true when the DynamoDB item is missing, and parseFallbackRegisterData returns a disabled config (and saves it) when fallbackEnabled is missing but additional_data is present. Consider updating the docs to match the actual behavior, or adjust the code to enforce the documented “not configured” state.
| When a store installs the app, Saleor can pass `additional_data` with `fallbackEnabled` and `fallbackRedirectEmail`. The app validates and stores this in DynamoDB. If no `additional_data` is provided or `fallbackEnabled` is missing, fallback is not configured. | |
| When a store installs the app, Saleor can pass `additional_data` with `fallbackEnabled` and `fallbackRedirectEmail`. The app validates and stores this in DynamoDB. If no `additional_data` is provided, the app assumes fallback is enabled (subject to the env vars being set). If `additional_data` is provided but `fallbackEnabled` is missing, the app stores a disabled fallback configuration by default. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| /* eslint-disable no-console */ | ||
| import { parseArgs } from "node:util"; | ||
|
|
||
| import { | ||
| CreateTableCommand, | ||
| DescribeTableCommand, | ||
| DynamoDBClient, | ||
| ResourceNotFoundException, | ||
| } from "@aws-sdk/client-dynamodb"; | ||
|
|
||
| const tableName = process.env.DYNAMODB_MAIN_TABLE_NAME ?? "smtp-main-table"; | ||
|
|
||
| try { | ||
| const { | ||
| values: { "endpoint-url": endpointUrl }, | ||
| } = parseArgs({ | ||
| args: process.argv.slice(2), | ||
| options: { | ||
| "endpoint-url": { | ||
| type: "string", | ||
| short: "e", | ||
| default: "http://localhost:8000", | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| console.log(`Starting DynamoDB setup with endpoint: ${endpointUrl}`); | ||
|
|
||
| const dynamoClient = new DynamoDBClient({ | ||
| endpoint: endpointUrl, | ||
| region: "localhost", | ||
| credentials: { | ||
| accessKeyId: "local", | ||
| secretAccessKey: "local", | ||
| }, | ||
| }); | ||
|
|
||
| const createTableIfNotExists = async (tableName: string) => { | ||
| try { | ||
| const possibleTable = await dynamoClient.send( | ||
| new DescribeTableCommand({ | ||
| TableName: tableName, | ||
| }), | ||
| ); | ||
|
|
||
| if (possibleTable.Table) { | ||
| console.log(`Table ${tableName} already exists - creation is skipped`); | ||
|
|
||
| return; | ||
| } | ||
| } catch (error) { | ||
| if (error instanceof ResourceNotFoundException) { | ||
| console.log(`Table ${tableName} does not exist, proceeding with creation.`); | ||
| } else { | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| const createTableCommand = new CreateTableCommand({ | ||
| TableName: tableName, | ||
| AttributeDefinitions: [ | ||
| { | ||
| AttributeName: "PK", | ||
| AttributeType: "S", | ||
| }, | ||
| { | ||
| AttributeName: "SK", | ||
| AttributeType: "S", | ||
| }, | ||
| ], | ||
| KeySchema: [ | ||
| { | ||
| AttributeName: "PK", | ||
| KeyType: "HASH", | ||
| }, | ||
| { | ||
| AttributeName: "SK", | ||
| KeyType: "RANGE", | ||
| }, | ||
| ], | ||
| ProvisionedThroughput: { | ||
| ReadCapacityUnits: 5, | ||
| WriteCapacityUnits: 5, | ||
| }, | ||
| }); | ||
|
|
||
| await dynamoClient.send(createTableCommand); | ||
| console.log(`Table ${tableName} created successfully`); | ||
| }; | ||
|
|
||
| await createTableIfNotExists(tableName); | ||
|
|
||
| console.log("DynamoDB setup completed successfully"); | ||
| process.exit(0); | ||
| } catch (error) { | ||
| console.error("Error setting up DynamoDB:", error); | ||
| process.exit(1); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spelling fixes needed in the changeset text (e.g. "receipient" → "recipient", "adderss" → "address"). These typos will end up in the published changelog.