Skip to content

Typy, šablony a jazykové verze ​

Notifikace mají 3 úrovně:

NotificationType   (SKUPINA / kategorie;  unikát: app + code)
        │  1 : N
        ▼
NotificationTemplate  (POJMENOVANÁ šablona;  unikát: app + type + key)
        │  1 : N
        ▼
TemplateLocale     (jazyková verze;  unikát: templateId + locale)

Příklad:

Typ "error"  (skupina — drží kanály a předvolby uživatele)
 ├─ Šablona "disk_full"   (key + name + variables)
 │    ├─ cs (subject/body)   ├─ en   └─ de
 └─ Šablona "auth_failed"
      ├─ cs   └─ en
  • Typ = skupina/kategorie. Drží uživatelské předvolby a kanály (emailEnabled, pushEnabled, userCanDisable, defaultEmail, defaultPush). Uživatel se odhlašuje z typu (= ze všech jeho šablon).
  • Šablona = pojmenovaná jednotka obsahu pod typem (key + name + description + variables[]). Variables patří šabloně (společné pro její jazyky).
  • Jazyková verze = subjectTemplate + bodyTemplate pro daný locale.

Jednotka, kterou aplikace odešle a kterou renderujeme = (app, type, template, locale).


1. Notifikační typy (skupina) ​

Typ definuje kategorii — co se oznamuje, jakými kanály a zda to může uživatel vypnout.

typescript
{
  id: number;
  applicationCode: string;
  code: string;              // unikátní kód v rámci aplikace (např. 'error')
  name: string;              // zobrazovaný název (pro UI preferencí)
  description: string | null;

  emailEnabled: boolean;     // email kanál povolen pro tento typ
  emailFromName: string | null; // volitelné jméno v hlavičce From
  pushEnabled: boolean;      // push kanál povolen pro tento typ
  userCanDisable: boolean;   // může uživatel typ (celou skupinu) vypnout?

  defaultEmail: boolean;     // výchozí stav email pro nové uživatele
  defaultPush: boolean;      // výchozí stav push pro nové uživatele
  selfServiceEnabled: boolean;
  system: boolean;           // read-only ochrana systémového typu
}

Předvolby a kanály jsou na typu, ne na šabloně. Uživatel se odhlašuje z celé skupiny (typu), ne z jednotlivých šablon.

emailFromName přepisuje pouze zobrazované jméno odesílatele a podporuje jediný token . Adresa schránky zůstává převzatá z SMTP konfigurace. Například Auth pro organizaci Jabcore a schránku report@jabcore.cloud odešle zprávu jako Auth Jabcore <report@jabcore.cloud>.

Display name se vybírá v pořadí typ → brand → globální SMTP. Token je povolen jen v display-name části; mailbox je vždy statický a parser odmítá CR/LF, neznámé tokeny i token uvnitř e-mailové adresy.

Skutečný výsledek lze bezpečně zobrazit ještě před uložením:

http
POST /api/admin/notifications/sender-preview
Authorization: Bearer <panel-token>
Content-Type: application/json

{
  "appCode": "auth",
  "typeCode": "auth_zitadel",
  "brand": "jabcore",
  "emailFromName": "Auth {{orgName}}"
}

Endpoint používá stejný resolver jako ostré odesílání a vrací pouze from, displayName, address, orgName a bezpečný popis source. SMTP heslo ani jiný secret se nenačítá do odpovědi. Vynechané emailFromName náhled počítá z uložené hodnoty; explicitní null ukáže fallback na brand/globální SMTP.

userCanDisable ​

Pokud userCanDisable = false, uživatel nemůže typ vypnout ve svých preferencích. Vhodné pro kritické systémové notifikace.

Systémový typ ​

system=true nastavuje pouze backendový seed. Takový typ nelze smazat ani mu změnit code/system; backend vždy vynutí e-mailový, povinný a service-to-service režim. Jeho obsah, překlady a emailFromName zůstávají editovatelné. Seedované šablony nelze smazat a poslední obsahovou locale nelze odstranit.

Správa typů ​

bash
POST   /api/admin/notifications/types/:appCode          # vytvoření
PUT    /api/admin/notifications/types/detail/:id        # aktualizace
DELETE /api/admin/notifications/types/detail/:id        # smazání (kaskádní)

Kaskádní smazání ​

DELETE /admin/notifications/types/detail/:id smaže typ i všechny jeho šablony i jejich jazykové verze (v transakci). code typu je neměnný: full-entity PUT smí zopakovat jeho současnou hodnotu jako idempotentní no-op, ale skutečná změna je odmítnuta.


2. Pojmenované šablony (mezivrstva) ​

Šablona je pojmenovaná jednotka obsahu pod typem.

typescript
{
  id: number;
  type: string;              // kód nadřazeného typu, např. 'error'
  key: string;               // slug unikátní v rámci (app, type), např. 'disk_full'
  name: string;              // zobrazovaný název
  description: string | null;
  variables: TemplateVariable[];  // kontrakt pro `data` (společné pro všechny jazyky)
}
  • key je slug ([a-z0-9_-]), unikátní v rámci (app, type), a je neměnný (rename se nepodporuje — vytvoř novou).
  • variables patří šabloně a jsou společné pro všechny její jazykové verze.

Správa šablon ​

MetodaCestaÚčel
GET/admin/notifications/types/:appCode/:type/templatesŠablony pod typem (vč. seznamu locale)
POST/admin/notifications/types/:appCode/:type/templatesVytvoř šablonu { key, name, description?, variables? }
PUT/admin/notifications/templates/:idUprav šablonu (name, description, variables)
DELETE/admin/notifications/templates/:idSmaž šablonu i s jazykovými verzemi (kaskáda)

GET vrací:

json
{
  "type": { "id": 3, "code": "error", "name": "Chyby", "...": "..." },
  "templates": [
    { "id": 10, "type": "error", "key": "disk_full", "name": "Disk je plný",
      "description": null, "variables": [ ... ], "locales": ["cs", "en"] }
  ]
}

Validace při zápisu ​

PodmínkaChyba (4xx, details.code)
type musí existovat jako NotificationType dané aplikaceUNKNOWN_NOTIFICATION_TYPE
key musí být validní slug a unikátní v (app, type)BAD_REQUEST

3. Jazykové verze (subject/body) ​

Každá šablona má 0..N jazykových verzí — jednu per locale.

typescript
{
  id: number;
  templateId: number;
  locale: string;            // "cs", "en", "de", ...
  subjectTemplate: string;   // Mustache šablona předmětu
  bodyTemplate: string;      // Mustache HTML šablona těla
}

Správa jazykových verzí ​

MetodaCestaÚčel
GET/admin/notifications/templates/:id/localesJazykové verze šablony
PUT (upsert)/admin/notifications/templates/:id/locales/:localeVytvoř/uprav verzi { subjectTemplate, bodyTemplate }
DELETE/admin/notifications/templates/:id/locales/:localeSmaž jednu jazykovou verzi
  • Upsert podle (templateId, locale) — když verze neexistuje, vytvoří (201); jinak aktualizuje (200). Žádné „already exists".
  • locale musí být povolený jazyk aplikace (Application.supportedLanguages; když aplikace nemá omezení, musí existovat v registru jazyků) → jinak 400 UNSUPPORTED_LOCALE. Normalizuje se na lowercase.

Rozlišení jazyka (fallback řetěz) — v rámci šablony ​

Při odeslání se vybírá jazyková verze zvolené šablony:

  1. verze v locale z request body (override), jinak v UserProfile.locale (default "en"),
  2. jinak po řetězu Language.fallback (např. hr → en),
  3. koncový safety-net: libovolná existující jazyková verze té šablony.

Řetěz se spravuje globálně v registru jazyků.


Odeslání: pole type + template ​

Aplikace cílí šablonu přes type (skupina) a template (key):

jsonc
POST /api/internal/notifications
{
  "recipientEmail": "petr@example.com",
  "appCode": "ui-template",
  "type": "error",          // SKUPINA
  "template": "disk_full",  // KONKRÉTNÍ ŠABLONA
  "locale": "cs",           // volitelné
  "data": { "path": "/var" }
}

Pravidla pro template (zpětná kompatibilita):

StavChování
template vyplněné, existujepoužije se šablona (app, type, template)
template vyplněné, neexistuje400 UNKNOWN_TEMPLATE, details.available = [klíče]
template chybí, typ má 1 šablonupoužije se ta jediná (pokrývá migrovaná data)
template chybí, typ má víc šablon400 TEMPLATE_REQUIRED, details.available = [klíče]
type === "generic"speciál — vestavěná šablona, bez DB

Validace data (per šablona) ​

required parametry se berou z variables zvolené šablony. Chybí-li → 400, code = "MISSING_TEMPLATE_VARIABLES", details.missing = […]. Systémové (appName, orgName, userName, userEmail) doplní backend (neposílat v data).


Parametry šablony (variables) ​

Šablona deklaruje, jaké parametry vyžaduje. Je to kontrakt, který odesílající aplikace musí splnit v poli data, a zároveň zdroj, ze kterého panel staví nápovědu a náhled.

typescript
{
  key: string;          // povinné, [A-Za-z0-9_.]; používá se jako {{key}}
  label?: string;       // popisek pro admina
  description?: string; // nápověda
  required?: boolean;   // default true
  example?: string;     // ukázková hodnota pro náhled
}

Parametry jsou jazykově nezávislé → patří šabloně a jsou shodné napříč všemi jejími jazykovými verzemi.

Systémové proměnné (appName, orgName, userName, userEmail) do variables nepatří — backend je doplňuje automaticky (viz Systémové proměnné).


Registr jazyků ​

Kurátovaný globální seznam jazyků platformy. Admin jazyky zakládá, pojmenovává a nastavuje jim fallback.

MetodaCestaTělo
GET/admin/languages→ Language[]
POST/admin/languages{ code, name, fallback? }
PUT/admin/languages/{code}{ name?, fallback? } (code neměnný)
DELETE/admin/languages/{code}—

Model: { code: string (lowercase, PK), name: string, fallback: string | null }. fallback = null = kořen. V řetězu nesmí vzniknout cyklus a registr musí mít alespoň jeden kořen. DELETE se odmítne, pokud je jazyk fallbackem jiného, je v Application.supportedLanguages nebo jde o poslední kořen.


Mustache šablony ​

Šablony používají jednoduchou Mustache syntaxi: vykreslí hodnotu.

Subject:

Nová objednávka č. {{orderNumber}} od {{customerName}}

Body (HTML):

html
<h1 style="color: {{brandPrimaryColor}}">Nová objednávka!</h1>
<p>Přijali jsme objednávku č. <strong>{{orderNumber}}</strong></p>
<p><a href="{{orderUrl}}" style="color: {{brandLinkColor}}">Zobrazit objednávku</a></p>
<hr>
<small>{{brandFooterText}}</small>

Automaticky dostupné proměnné (brand context) ​

ProměnnáObsah
Hlavní barva brandu (hex)
Tmavší varianta hlavní barvy
Barva pozadí
Barva textu
Barva odkazů
URL loga brandu
Text v patičce
Název brandu
Odesílací email

Brand se načte dle UserProfile.brand příjemce. Pokud brand neexistuje, použije se výchozí brand.

Systémové proměnné ​

Backend doplňuje do každé šablony tyto proměnné. Odesílající aplikace je nemá posílat v data — jakákoliv hodnota poslaná pod těmito klíči je přepsána:

ProměnnáObsah
Název aplikace (z Application.name)
Název organizace/brandu příjemce (z Brand.name)
Jméno příjemce (UserProfile.displayName, fallback na e-mail)
E-mail příjemce

Tyto proměnné se do variables neuvádějí.


Preview šablony ​

bash
POST /api/admin/notifications/templates/preview
{
  "subjectTemplate": "Objednávka č. {{orderNumber}}",
  "bodyTemplate": "<h1>Přijata!</h1><p>{{orderNumber}} — {{totalAmount}} Kč</p>",
  "data": { "orderNumber": "TEST-001", "totalAmount": "5000" },
  "brand": "jabcore"
}

Preview renderuje libovolný subject/body — není vázaný na uloženou šablonu.


Checklist pro nový notifikační typ ​

  • [ ] Vytvořit NotificationType (POST /admin/notifications/types/:appCode)
  • [ ] Vytvořit pojmenovanou šablonu (POST /admin/notifications/types/:appCode/:type/templates) s key + name + variables
  • [ ] Přidat jazykové verze (PUT /admin/notifications/templates/:id/locales/:locale) — ručně nebo přes Lexicon auto-překlad
  • [ ] Ověřit fallback řetěz v /admin/languages
  • [ ] Otestovat preview s testovacími daty
  • [ ] Ověřit, že aplikace posílá type + template + správná data
  • [ ] Zkontrolovat emailEnabled / pushEnabled a userCanDisable na typu

Auto-překlad přes Lexicon ​

Šablony lze automaticky dopřeložit přes Lexicon API (DeepL pod kapotou):

  • GET /admin/notifications/lexicon/languages — seznam podporovaných cílových jazyků.
  • POST /admin/notifications/lexicon/translate-template — přeloží subjectTemplate + bodyTemplate. Placeholdery jsou před překladem maskované a po překladu obnovené.

Auto-překlad šablonu neukládá — vrátí přeložený text ke kontrole. Konfigurace: LEXICON_API_URL / LEXICON_API_KEY.

Jabcore Platform — interní dokumentace