graph + mail dispatcher, test-email endpoint, notifications global
This commit is contained in:
+80
-4
@@ -1,8 +1,10 @@
|
||||
# email
|
||||
|
||||
Wysyłka maili przez SMTP z SiteIntegrations, w runtime (bez Payload email
|
||||
adaptera). Edytor zmienia SMTP w panelu — następny mail idzie z nowymi
|
||||
ustawieniami, bez restartu.
|
||||
Wysyłka maili z dwoma transportami do wyboru: **SMTP z panelu**
|
||||
(`panelSmtpAdapter`, uniwersalny) albo **Microsoft Graph** (`graphAdapter`,
|
||||
przez Exchange/M365). Oba implementują ten sam interfejs `PayloadEmailAdapter`,
|
||||
więc `payload.sendEmail` i maile z formularzy działają niezależnie od wyboru.
|
||||
Klient/projekt wybiera transport w configu.
|
||||
|
||||
## Zależność
|
||||
|
||||
@@ -73,4 +75,78 @@ resetu hasła i weryfikacji konta. Adapter czyta konfigurację przy każdym
|
||||
wysłaniu, więc zmiana skrzynki w panelu działa bez restartu.
|
||||
|
||||
Bez adaptera Payload używa mocka, który tylko loguje do konsoli — maile
|
||||
form-buildera nie wyjdą.
|
||||
form-buildera nie wyjdą.
|
||||
|
||||
## Maskowanie sekretów w panelu (MaskedField)
|
||||
|
||||
Wrażliwe pola w Site Integrations (smtpPassword, r2SecretAccessKey,
|
||||
turnstileSecretKey) są maskowane w UI — pokazują `••••` zamiast plaintextu, z
|
||||
przyciskiem Reveal/Hide. To maskowanie UI, NIE hashowanie ani szyfrowanie:
|
||||
wartość w bazie jest plaintext (musi być odzyskiwalna do autentykacji SMTP/R2).
|
||||
Chroni przed patrzeniem przez ramię i przypadkowym pokazaniem panelu.
|
||||
|
||||
Podpięte przez `admin.components.Field: '@intecion/ipal-kit/client#MaskedField'`.
|
||||
Działa na dowolnym polu `text`. Po wpięciu w projekcie może być konieczne
|
||||
`payload generate:importmap`, żeby panel rozpoznał komponent.
|
||||
|
||||
> Główną ochroną sekretów pozostaje `read: isAdmin` na globalu SiteIntegrations
|
||||
> (anonim nie dostaje). Maskowanie to warstwa dodatkowa (shoulder-surfing), nie
|
||||
> ochrona bazy — przy wycieku DB sekrety są czytelne.
|
||||
|
||||
|
||||
## Adapter — Microsoft Graph (Exchange / M365)
|
||||
|
||||
Alternatywa dla SMTP: wysyłka przez Microsoft Graph API, przez skrzynkę w
|
||||
Waszym (agencyjnym) tenancie M365. Wszystkie maile z formularzy wszystkich
|
||||
projektów idą przez JEDNĄ skrzynkę nadawczą (np. `[email protected]`).
|
||||
|
||||
### Podział konfiguracji (celowy)
|
||||
|
||||
**Sekrety w `.env`** (agencyjne — Wasz Exchange, klient nie widzi):
|
||||
|
||||
```bash
|
||||
GRAPH_TENANT_ID=...
|
||||
GRAPH_CLIENT_ID=...
|
||||
GRAPH_CLIENT_SECRET=...
|
||||
GRAPH_SENDER=[email protected] # jedna skrzynka dla wszystkich projektów
|
||||
```
|
||||
|
||||
**From-display w panelu** (per projekt): czyta istniejące `smtpFromAddress` /
|
||||
`smtpFromName` z SiteIntegrations — bo „from" to ten sam koncept niezależnie od
|
||||
transportu. Nie trzeba nowego pola.
|
||||
|
||||
### Wpięcie — wybór transportu
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
import { panelSmtpAdapter, graphAdapter } from '@intecion/ipal-kit'
|
||||
|
||||
email: process.env.GRAPH_CLIENT_ID
|
||||
? graphAdapter() // Graph, gdy sekrety w .env
|
||||
: panelSmtpAdapter(), // SMTP z panelu (fallback)
|
||||
```
|
||||
|
||||
### Setup Azure / Exchange (jednorazowo, Wasza strona, POZA kodem)
|
||||
|
||||
1. **App registration** w Azure AD → `tenantId`, `clientId`
|
||||
2. **Client secret** → `clientSecret`
|
||||
3. **API Permissions** → Microsoft Graph → **Application** → `Mail.Send` →
|
||||
**Grant admin consent** (bez tego: `Insufficient privileges`)
|
||||
4. **Exchange Admin Center** → skrzynka `[email protected]` → Mailbox
|
||||
Delegation → aplikacja do **"Send As"** (bez tego: `ErrorAccessDenied`)
|
||||
|
||||
### Szczegóły techniczne
|
||||
|
||||
- Auth: client credentials flow, scope `https://graph.microsoft.com/.default`
|
||||
(NIE `Mail.Send` — Azure odrzuca, AADSTS1002012)
|
||||
- Wysyłka: `POST /users/{sender}/sendMail` (NIE `/me` — app-only nie ma „me")
|
||||
- Sukces: HTTP 202 (pusty body)
|
||||
- Czysty REST (fetch), zero bibliotek Microsoft, zero nowych zależności
|
||||
|
||||
### PUŁAPKA — from vs Send-As
|
||||
|
||||
Jeśli `from` w panelu = cudza domena (np. `[email protected]`), a sender =
|
||||
`[email protected]` — Exchange zablokuje, chyba że aplikacja ma Send-As na tę
|
||||
domenę. Najbezpieczniej: `from` = `GRAPH_SENDER` (Wasza skrzynka), a adres
|
||||
klienta w `replyTo` (odpowiedzi trafią do klienta). Wtedy Send-As na cudze
|
||||
domeny nie jest potrzebny.
|
||||
Reference in New Issue
Block a user