diff --git a/README.md b/README.md
index 3af7f12..9ebd137 100644
--- a/README.md
+++ b/README.md
@@ -1,112 +1,140 @@
-
-

-
+# Saleor Core Extensions
-
-
Saleor App Template
-
+A Saleor app that sends email notifications for order events (created, fulfilled, cancelled) using React Email templates.
-
-
Bare-bones boilerplate for writing Saleor Apps with Next.js.
-
+## Features
-
+- **Order Created** - Sends confirmation emails to customer and admin
+- **Order Fulfilled** - Sends shipping notification with tracking
+- **Order Cancelled** - Sends cancellation notification
+- **Multi-language** - Supports EN, SR, DE, FR
+- **React Email** - Professional HTML emails with responsive design
-> [!TIP]
-> Questions or issues? Check our [discord](https://discord.gg/H52JTZAtSH) channel for help.
+## Installation
-### What is Saleor App
+### Option 1: Install via Manifest URL
-Saleor App is the fastest way of extending Saleor with custom logic using [asynchronous](https://docs.saleor.io/docs/3.x/developer/extending/apps/asynchronous-webhooks) and [synchronous](https://docs.saleor.io/docs/3.x/developer/extending/apps/synchronous-webhooks/key-concepts) webhooks (and vast Saleor's API). In most cases, creating an App consists of two tasks:
+In your Saleor Dashboard, go to Apps β Install App β Enter manifest URL:
-- Writing webhook's code executing your custom logic.
-- Developing configuration UI to be displayed in Saleor Dashboard via specialized view (designated in the App's manifest).
+```
+https://your-app-domain.com/api/manifest
+```
-### What's included?
+### Option 2: Manual Installation
-- π Communication between Saleor instance and Saleor App
-- π Manifest with webhooks using custom query
+```bash
+# Clone the repository
+git clone https://github.com/your-org/saleor-core-extensions.git
+cd saleor-core-extensions
-### Why Next.js
+# Install dependencies
+pnpm install
-You can use any preferred technology to create Saleor Apps, but Next.js is among the most efficient for two reasons. The first is the simplicity of maintaining your API endpoints/webhooks and your apps' configuration React front-end in a single, well-organized project. The second reason is the ease and quality of local development and deployment.
+# Build Docker image
+docker build -t ghcr.io/your-org/saleor-core-extensions:latest .
-### Learn more about Apps
+# Deploy to your K8s cluster
+kubectl apply -f deployment.yaml
+```
-[Apps guide](https://docs.saleor.io/docs/3.x/developer/extending/apps/key-concepts)
+## Configuration
+
+### Environment Variables
+
+| Variable | Required | Default | Description |
+|----------|----------|---------|-------------|
+| `SALEOR_API_URL` | Yes | - | Internal K8s URL for Saleor API (e.g., `http://saleor-api.saleor:8000/graphql/`) |
+| `ALLOWED_SALEOR_URLS` | No | `http://localhost:3000` | Comma-separated list of allowed Saleor API URLs |
+| `RESEND_API_KEY` | Yes | - | API key from [Resend.com](https://resend.com) |
+| `FROM_EMAIL` | No | `support@mail.manoonoils.com` | Sender email address |
+| `FROM_NAME` | No | `ManoonOils` | Sender name |
+| `ADMIN_EMAILS` | Yes | - | Comma-separated admin emails for notifications |
+| `SITE_URL` | No | `https://dev.manoonoils.com` | Public store URL |
+| `DASHBOARD_URL` | No | `https://dashboard.manoonoils.com` | Saleor dashboard URL |
+| `APP_IFRAME_BASE_URL` | Yes | - | Public URL where app is hosted |
+| `APP_API_BASE_URL` | Yes | - | Same as APP_IFRAME_BASE_URL |
+| `AUTH_DATA_FILE_PATH` | No | `/tmp/.auth-data.json` | Path for auth data storage |
+| `EMAIL_LOGO_URL` | No | - | URL to company logo for emails |
+| `EMAIL_COMPANY_NAME` | No | `Store` | Company name in emails |
+| `EMAIL_FOOTER` | No | auto | Footer text in emails |
+
+### Kubernetes Deployment
+
+```yaml
+env:
+ - name: SALEOR_API_URL
+ value: "http://saleor-api.saleor:8000/graphql/"
+ - name: APP_IFRAME_BASE_URL
+ value: "https://your-app.domain.com"
+ - name: APP_API_BASE_URL
+ value: "https://your-app.domain.com"
+ - name: RESEND_API_KEY
+ valueFrom:
+ secretKeyRef:
+ name: core-extensions-secrets
+ key: resend-api-key
+ - name: ADMIN_EMAILS
+ value: "admin@example.com"
+```
+
+## Architecture
+
+```
+βββββββββββββββββββ Webhooks ββββββββββββββββββββ
+β Saleor Cloud β ββββββββββββββββΊ β Core Extensions β
+β β β App β
+βββββββββββββββββββ ββββββββββ¬ββββββββββ
+ β² β
+ β βΌ
+ β ββββββββββββββββ
+ β β Resend β
+ βββββββ GraphQL API ββββββββββββ (Email) β
+ ββββββββββββββββ
+```
+
+### Internal Networking
+
+The app uses `SALEOR_API_URL` to communicate with Saleor API internally, avoiding Cloudflare HTTP restrictions. The stored auth token is reused while the env var controls the API endpoint.
## Development
-#### Running app locally in development containers
-
-The easiest way of running app for local development is to use [development containers](https://containers.dev/).
-If you have Visual Studio Code follow their [guide](https://code.visualstudio.com/docs/devcontainers/containers#_quick-start-open-an-existing-folder-in-a-container) on how to open existing folder in container.
-
-Development container only creates container, you still need to start the server.
-
-Development container will have port opened:
-
-1. `3000` - were app dev server will listen to requests
-
-### Requirements
-
-Before you start, make sure you have installed:
-
-- [Node.js 22](https://nodejs.org/en/)
-- [pnpm 9](https://pnpm.io/)
-
-1. Install the dependencies by running:
-
-```
+```bash
+# Install dependencies
pnpm install
-```
-2. Start the local server with:
-
-```
+# Start development server
pnpm dev
+
+# Generate GraphQL types
+pnpm generate
+
+# Build for production
+pnpm build
```
-3. Expose local environment using tunnel:
- Use tunneling tools like [localtunnel](https://github.com/localtunnel/localtunnel) or [ngrok](https://ngrok.com/).
-
-4. Install the application in your dashboard:
-
-If you use Saleor Cloud or your local server is exposed, you can install your app by following this link:
+## Project Structure
```
-[YOUR_SALEOR_DASHBOARD_URL]/apps/install?manifestUrl=[YOUR_APP_TUNNEL_MANIFEST_URL]
+src/
+βββ emails/
+β βββ BaseLayout.tsx # Email layout with logo/footer
+β βββ OrderConfirmation.tsx # Order confirmation email
+β βββ OrderShipped.tsx # Order shipped email
+β βββ OrderCancelled.tsx # Order cancelled email
+βββ lib/
+β βββ resend.ts # Email sending logic
+β βββ create-graphq-client.ts
+βββ pages/
+β βββ api/
+β β βββ manifest.ts # App manifest
+β β βββ register.ts # App registration
+β βββ webhooks/
+β βββ order-created.ts
+β βββ order-fulfilled.ts
+β βββ order-cancelled.ts
+βββ saleor-app.ts # APL configuration
```
-This template host manifest at `/api/manifest`
+## License
-You can also install application using GQL or command line. Follow the guide [how to install your app](https://docs.saleor.io/docs/3.x/developer/extending/apps/installing-apps#installation-using-graphql-api) to learn more.
-
-### Generated schema and typings
-
-This project uses a `generate` npm script command to:
-
-- Generate GraphQL schema and typed functions from Saleor's GraphQL endpoint.
-- Generate types for Saleor sync webhook responses from JSON schema
-
-Commit the `generated` folder to your repo as they are necessary for queries and keeping track of the GraphQL / JSON schema changes.
-
-To generate GraphQL types we are using [GraphQL Codegen](https://www.graphql-code-generator.com/). For generating types from JSON schema we use [json-schema-to-typescript](https://www.npmjs.com/package/json-schema-to-typescript).
-
-### Storing registration data - APL
-
-During the registration process, Saleor API passes the auth token to the app. With this token App can query Saleor API with privileged access (depending on requested permissions during the installation).
-To store this data, app-template use a different [APL interfaces](https://docs.saleor.io/developer/extending/apps/developing-apps/app-sdk/apl).
-
-The choice of the APL is made using the `APL` environment variable. If the value is not set, FileAPL is used. Available choices:
-
-- `file`: no additional setup is required. Good choice for local development. It can't be used for multi tenant-apps or be deployed (not intended for production)
-- `upstash`: use [Upstash](https://upstash.com/) Redis as storage method. Free account required. It can be used for development and production and supports multi-tenancy. Requires `UPSTASH_URL` and `UPSTASH_TOKEN` environment variables to be set
-
-If you want to use your own database, you can implement your own APL. [Check the documentation to read more](https://docs.saleor.io/developer/extending/apps/developing-apps/app-sdk/apl).
+MIT
diff --git a/src/emails/BaseLayout.tsx b/src/emails/BaseLayout.tsx
index 3bc4a78..2cf650b 100644
--- a/src/emails/BaseLayout.tsx
+++ b/src/emails/BaseLayout.tsx
@@ -21,25 +21,35 @@ interface BaseLayoutProps {
const translations: Record = {
sr: {
- footer: "ManoonOils - Prirodna kozmetika | www.manoonoils.com",
- company: "ManoonOils",
+ footer: "",
+ company: "",
},
en: {
- footer: "ManoonOils - Natural Cosmetics | www.manoonoils.com",
- company: "ManoonOils",
+ footer: "",
+ company: "",
},
de: {
- footer: "ManoonOils - NatΓΌrliche Kosmetik | www.manoonoils.com",
- company: "ManoonOils",
+ footer: "",
+ company: "",
},
fr: {
- footer: "ManoonOils - CosmΓ©tiques Naturels | www.manoonoils.com",
- company: "ManoonOils",
+ footer: "",
+ company: "",
},
};
-export function BaseLayout({ children, previewText, language, siteUrl }: BaseLayoutProps) {
+const COMPANY_NAME = process.env.EMAIL_COMPANY_NAME || "Store";
+const LOGO_URL = process.env.EMAIL_LOGO_URL || "";
+const DEFAULT_FOOTER = process.env.EMAIL_FOOTER || `${COMPANY_NAME} | ${process.env.SITE_URL || ""}`;
+
+function getFooter(language: string): string {
const t = translations[language] || translations.en;
+ const footer = t.footer || DEFAULT_FOOTER;
+ return footer.replace("{company}", COMPANY_NAME).replace("{siteUrl}", process.env.SITE_URL || "");
+}
+
+export function BaseLayout({ children, previewText, language, siteUrl }: BaseLayoutProps) {
+ const footer = getFooter(language);
return (
@@ -47,18 +57,20 @@ export function BaseLayout({ children, previewText, language, siteUrl }: BaseLay
{previewText}
-
-
-
+ {LOGO_URL && (
+
+
+
+ )}
{children}
diff --git a/src/pages/api/register.ts b/src/pages/api/register.ts
index 1ccd995..665a999 100644
--- a/src/pages/api/register.ts
+++ b/src/pages/api/register.ts
@@ -1,21 +1,11 @@
-// Patch fetch to force HTTPS for api.manoonoils.com
-const originalFetch = global.fetch;
-global.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
- let url = input.toString();
- if (url.startsWith('http://api.manoonoils.com/')) {
- url = url.replace('http://', 'https://');
- input = url;
- }
- return originalFetch(input, init);
-};
-
import { createAppRegisterHandler } from "@saleor/app-sdk/handlers/next";
import { saleorApp } from "@/saleor-app";
+const allowedSaleorUrls = process.env.ALLOWED_SALEOR_URLS
+ ? process.env.ALLOWED_SALEOR_URLS.split(",").map((url) => url.trim())
+ : ["http://localhost:3000", "https://*.saleor.cloud"];
+
export default createAppRegisterHandler({
apl: saleorApp.apl,
- allowedSaleorUrls: [
- "https://api.manoonoils.com/graphql/",
- "http://api.manoonoils.com/graphql/",
- ],
+ allowedSaleorUrls,
});
\ No newline at end of file