email és dic

This commit is contained in:
magdo
2026-03-03 18:37:08 +01:00
parent ffca701b84
commit afc3777ac9
7 changed files with 4350 additions and 0 deletions
+486
View File
@@ -0,0 +1,486 @@
\section{CORS}
\begin{frame}[fragile]{Mi az a CORS?}
\begin{block}{Cross-Origin Resource Sharing}
\begin{itemize}
\item Biztonsági mechanizmus, amely szabályozza a kereszt-eredet HTTP kéréseket
\item A böngészők alapértelmezés szerint blokkolják a különböző eredetről (origin) érkező kéréseket
\item Az \textbf{origin} = protokoll + domain + port
\item CORS policy lehetővé teszi biztonságos erőforrás-megosztást különböző domainek között
\end{itemize}
\end{block}
\begin{exampleblock}{Példák különböző originekre}
\begin{itemize}
\item \texttt{https://example.com:443} $\neq$ \texttt{http://example.com:80} (protokoll)
\item \texttt{https://example.com} $\neq$ \texttt{https://api.example.com} (subdomain)
\item \texttt{https://example.com:3000} $\neq$ \texttt{https://example.com:4000} (port)
\end{itemize}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Miért van szükség CORS-ra?}
\begin{block}{Same-Origin Policy (SOP)}
\begin{itemize}
\item Böngészők biztonsági mechanizmusa
\item Megakadályozza, hogy rosszindulatú scriptek más domainek adataihoz férjenek hozzá
\item Alapértelmezés szerint csak azonos origin-ről tölthet le erőforrásokat
\end{itemize}
\end{block}
\begin{alertblock}{Probléma}
Modern alkalmazásoknál gyakori:
\begin{itemize}
\item Frontend: \texttt{http://localhost:3000}
\item Backend API: \texttt{http://localhost:5000}
\item Különböző origin $\rightarrow$ CORS hiba!
\end{itemize}
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{CORS működése - Simple Request}
\begin{block}{Egyszerű kérések}
Olyan kérések, amelyek nem váltanak ki preflight kérést:
\begin{itemize}
\item Metódus: GET, HEAD, vagy POST
\item Content-Type: \texttt{application/x-www-form-urlencoded}, \texttt{multipart/form-data}, \texttt{text/plain}
\item Csak standard headerek
\end{itemize}
\end{block}
\begin{exampleblock}{Kliens oldali kérés}
\small
\begin{lstlisting}[language=JavaScript]
fetch('http://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS működése - Preflight Request}
\begin{block}{Preflight kérés}
\begin{itemize}
\item Komplex kérések előtt a böngésző automatikusan küld egy OPTIONS kérést
\item Ellenőrzi, hogy a szerver engedélyezi-e a tényleges kérést
\item PUT, DELETE, PATCH metódusok vagy custom headerek esetén
\end{itemize}
\end{block}
\begin{columns}[T]
\begin{column}{0.48\textwidth}
\textbf{1. Preflight (OPTIONS)}
\begin{itemize}
\item Access-Control-Request-Method
\item Access-Control-Request-Headers
\end{itemize}
\end{column}
\begin{column}{0.48\textwidth}
\textbf{2. Actual Request}
\begin{itemize}
\item A tényleges HTTP kérés
\item Csak akkor megy el, ha a preflight engedélyező választ adott
\end{itemize}
\end{column}
\end{columns}
\end{frame}
\begin{frame}[fragile]{CORS Headers - Szerver válaszok}
\begin{block}{Legfontosabb CORS headerek}
\begin{description}
\item[\texttt{Access-Control-Allow-Origin}] Megadja, mely origin-ek férhetnek hozzá
\item[\texttt{Access-Control-Allow-Methods}] Engedélyezett HTTP metódusok
\item[\texttt{Access-Control-Allow-Headers}] Engedélyezett HTTP headerek
\item[\texttt{Access-Control-Allow-Credentials}] Cookie-k küldése engedélyezett-e
\item[\texttt{Access-Control-Max-Age}] Preflight válasz cache ideje (másodperc)
\end{description}
\end{block}
\end{frame}
\begin{frame}[fragile]{CORS engedélyezése Express.js-ben - Manuális (1/2)}
\begin{exampleblock}{Egyszerű megoldás - minden origin engedélyezése}
\small
\begin{lstlisting}[language=JavaScript]
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods',
'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers',
'Content-Type, Authorization');
next();
});
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS engedélyezése Express.js-ben - Manuális (2/2)}
\begin{exampleblock}{Preflight request kezelése}
\small
\begin{lstlisting}[language=JavaScript]
app.use((req, res, next) => {
// Preflight request kezelése
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
\end{lstlisting}
\end{exampleblock}
\begin{alertblock}{Figyelem!}
A \texttt{*} wildcard nem biztonságos production környezetben!
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{CORS engedélyezése Express.js-ben - CORS csomag}
\begin{block}{Telepítés}
\small
\begin{lstlisting}[language=bash]
npm install cors
\end{lstlisting}
\end{block}
\end{frame}
\begin{frame}[fragile]{CORS csomag - Alapértelmezett használat}
\begin{exampleblock}{Minden origin engedélyezése}
\small
\begin{lstlisting}[language=JavaScript]
const express = require('express');
const cors = require('cors');
const app = express();
// Minden origin engedélyezése
app.use(cors());
app.get('/api/data', (req, res) => {
res.json({ message: 'CORS enabled!' });
});
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS konfiguráció - Specifikus origin}
\begin{exampleblock}{Csak egy origin engedélyezése}
\small
\begin{lstlisting}[language=JavaScript]
const corsOptions = {
origin: 'https://example.com'
};
app.use(cors(corsOptions));
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS konfiguráció - Több origin (1/2)}
\begin{exampleblock}{Engedélyezett origin-ek listája}
\small
\begin{lstlisting}[language=JavaScript]
const allowedOrigins = [
'https://example.com',
'https://app.example.com',
'http://localhost:3000'
];
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS konfiguráció - Több origin (2/3)}
\begin{exampleblock}{Dinamikus origin validáció}
\small
\begin{lstlisting}[language=JavaScript]
const corsOptions = {
origin: (origin, callback) => {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
};
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS konfiguráció - Több origin (3/3)}
\begin{exampleblock}{CORS middleware beállítása}
\small
\begin{lstlisting}[language=JavaScript]
// Korábban definiált corsOptions használata
app.use(cors(corsOptions));
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS konfiguráció - Credentials}
\begin{block}{Mi az a credentials?}
\begin{itemize}
\item Cookie-k, HTTP authentication, client-side SSL certificates
\item Alapértelmezés szerint a böngésző nem küldi el ezeket cross-origin kéréseknél
\end{itemize}
\end{block}
\begin{exampleblock}{Szerver oldal - Express}
\small
\begin{lstlisting}[language=JavaScript]
const corsOptions = {
origin: 'https://example.com',
credentials: true // Cookie-k engedélyezése
};
app.use(cors(corsOptions));
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS Credentials - Kliens oldal}
\begin{exampleblock}{Kliens oldal - Fetch API}
\small
\begin{lstlisting}[language=JavaScript]
fetch('https://api.example.com/data', {
credentials: 'include' // Cookie-k küldése
})
.then(response => response.json())
.then(data => console.log(data));
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS konfiguráció - További opciók (1/2)}
\begin{exampleblock}{Részletes konfiguráció}
\small
\begin{lstlisting}[language=JavaScript]
const corsOptions = {
origin: 'https://example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
exposedHeaders: ['X-Total-Count'],
credentials: true
};
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS konfiguráció - További opciók (2/2)}
\begin{exampleblock}{Cache és preflight beállítások}
\small
\begin{lstlisting}[language=JavaScript]
const corsOptions = {
// ...előző beállítások
maxAge: 3600, // Preflight cache 1 órára
preflightContinue: false,
optionsSuccessStatus: 204
};
app.use(cors(corsOptions));
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS-specifikus route-okhoz (1/3)}
\begin{exampleblock}{Public endpoint CORS-szal}
\small
\begin{lstlisting}[language=JavaScript]
const express = require('express');
const cors = require('cors');
const app = express();
// Csak public API-hoz engedélyezve
app.get('/api/public', cors(), (req, res) => {
res.json({ message: 'Public data' });
});
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS-specifikus route-okhoz (2/3)}
\begin{exampleblock}{Protected endpoint CORS nélkül}
\small
\begin{lstlisting}[language=JavaScript]
// Protected endpoint CORS nélkül
app.get('/api/private', (req, res) => {
res.json({ message: 'Private data' });
});
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS-specifikus route-okhoz (3/3)}
\begin{exampleblock}{Specifikus CORS konfig egy route-hoz}
\small
\begin{lstlisting}[language=JavaScript]
const corsOptionsRestricted = {
origin: 'https://trusted.example.com'
};
app.post('/api/sensitive',
cors(corsOptionsRestricted),
(req, res) => {
res.json({ message: 'Sensitive operation' });
}
);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS hibakezelés (1/2)}
\begin{exampleblock}{Engedélyezett origin-ek}
\small
\begin{lstlisting}[language=JavaScript]
const allowedOrigins = [
'https://example.com',
'http://localhost:3000'
];
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS hibakezelés (2/3)}
\begin{exampleblock}{Dinamikus origin ellenőrzés hibakezeléssel}
\small
\begin{lstlisting}[language=JavaScript]
const corsOptions = {
origin: (origin, callback) => {
if (!origin) return callback(null, true);
if (allowedOrigins.includes(origin)) {
callback(null, true);
} else {
const msg = `Origin ${origin} not allowed`;
callback(new Error(msg), false);
}
},
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS hibakezelés (3/3)}
\begin{exampleblock}{CORS konfiguráció befejezése}
\small
\begin{lstlisting}[language=JavaScript]
// ...folytatás
credentials: true
};
app.use(cors(corsOptions));
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Gyakori CORS hibák és megoldások (1/2)}
\begin{alertblock}{1. "No 'Access-Control-Allow-Origin' header"}
\textbf{Ok:} A szerver nem küldi vissza a CORS headereket\\
\textbf{Megoldás:} CORS middleware beállítása
\end{alertblock}
\begin{alertblock}{2. "Credentials + wildcard origin"}
\textbf{Ok:} \texttt{credentials: true} és \texttt{origin: '*'} együttes használata\\
\textbf{Megoldás:} Specifikus origin megadása credentials használatakor
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Gyakori CORS hibák és megoldások (2/2)}
\begin{alertblock}{3. "Preflight request failed"}
\textbf{Ok:} OPTIONS kérés nem megfelelő válasza\\
\textbf{Megoldás:} OPTIONS metódus explicit kezelése, megfelelő headerek
\end{alertblock}
\begin{alertblock}{4. "Custom headers blokkolva"}
\textbf{Ok:} Custom header nincs az \texttt{allowedHeaders} listában\\
\textbf{Megoldás:} Header hozzáadása az \texttt{allowedHeaders}-hoz
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Best Practices}
\begin{block}{Biztonsági ajánlások}
\footnotesize
\begin{enumerate}
\item \textbf{Soha ne használj} \texttt{origin: '*'} production környezetben, főleg credentials esetén
\item \textbf{Whitelist megközelítés}: csak konkrét, megbízható origin-ek engedélyezése
\item \textbf{Minimális jogosultságok}: csak a szükséges metódusok és headerek engedélyezése
\item \textbf{Credentials óvatos kezelése}: csak akkor engedélyezd, ha feltétlenül szükséges
\item \textbf{maxAge beállítás}: csökkenti a preflight kérések számát
\end{enumerate}
\end{block}
\end{frame}
\begin{frame}[fragile]{Production konfiguráció}
\begin{exampleblock}{Ajánlott production konfiguráció}
\small
\begin{lstlisting}[language=JavaScript]
const corsOptions = {
origin: process.env.ALLOWED_ORIGINS?.split(',')
|| [],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400 // 24 óra
};
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS proxy fejlesztési környezetben (1/2)}
\begin{block}{Development server proxy}
Frontend fejlesztési szerverekben (pl. Vite, Create React App) konfigurálható proxy
\end{block}
\begin{exampleblock}{Vite proxy alapkonfiguráció (vite.config.js)}
\small
\begin{lstlisting}[language=JavaScript]
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true
}
}
}
};
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{CORS proxy fejlesztési környezetben (2/2)}
\begin{exampleblock}{Rewrite funkció hozzáadása}
\small
\begin{lstlisting}[language=JavaScript]
// Proxy konfigurációban:
proxy: {
'/api': {
rewrite: (path) =>
path.replace(/^\/api/, '')
}
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}{Proxy működése}
\begin{itemize}
\item Kliens \texttt{http://localhost:3000} $\rightarrow$ \texttt{/api/users}
\item Proxy továbbítja \texttt{http://localhost:5000} $\rightarrow$ \texttt{/users}
\item Nincs CORS probléma, mert a böngésző szempontjából azonos origin
\item Csak development használatra, production esetén backend CORS konfiguráció szükséges
\end{itemize}
\end{frame}
\begin{frame}{Összefoglalás}
\begin{block}{CORS lényege}
\begin{itemize}
\item Biztonsági mechanizmus a cross-origin kérések szabályozására
\item Same-Origin Policy kiegészítése, nem helyettesítése
\item Preflight requests komplex kérések előtt
\item Explicit szerver-oldali engedélyezés szükséges
\end{itemize}
\end{block}
\begin{block}{Implementáció}
\begin{itemize}
\item Express.js-ben: \texttt{cors} middleware csomag
\item Konfiguráció: origin, methods, headers, credentials
\item Fejlesztés: proxy használata, production: explicit whitelist
\item Biztonsági szempontok elsődlegesek!
\end{itemize}
\end{block}
\end{frame}
@@ -0,0 +1,816 @@
\section{Dependency Injection}
\begin{frame}{Mi az a Dependency Injection (DI)?}
\begin{block}{Definíció}
A \textbf{Dependency Injection} egy tervezési minta, ahol az objektumok nem maguk hozzák létre a függőségeiket, hanem kívülről kapják meg azokat.
\end{block}
\end{frame}
\begin{frame}{DI előnyei és hátrányai}
\begin{columns}[T]
\begin{column}{0.48\textwidth}
\begin{alertblock}{Nélküle (rossz)}
\begin{itemize}
\item Szoros kapcsolás (tight coupling)
\item Nehéz tesztelni
\item Nehéz karbantartani
\item Függőségek rejtettek
\end{itemize}
\end{alertblock}
\end{column}
\begin{column}{0.48\textwidth}
\begin{exampleblock}{Vele (jó)}
\begin{itemize}
\item Laza kapcsolás (loose coupling)
\item Könnyű tesztelni
\item Könnyen karbantartható
\item Függőségek explicit módon láthatók
\end{itemize}
\end{exampleblock}
\end{column}
\end{columns}
\end{frame}
\begin{frame}[fragile]{Probléma DI nélkül (1/3)}
\begin{alertblock}{Szoros kapcsolás példa - Osztály}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
constructor() {
// A UserService közvetlenül létrehozza
// a függőségeit
this.database = new MySQLDatabase();
this.emailService = new EmailService();
this.logger = new Logger();
}
}
\end{lstlisting}
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Probléma DI nélkül (2/3)}
\begin{alertblock}{Használat}
\small
\begin{lstlisting}[language=JavaScript]
const userService = new UserService();
async createUser(userData) {
this.logger.log('Creating user...');
const user = await this.database.save(userData);
await this.emailService.sendWelcomeEmail(
user.email
);
return user;
}
\end{lstlisting}
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Probléma DI nélkül (3/3)}
\end{alertblock}
\begin{alertblock}{Problémák}
\begin{itemize}
\item Nem lehet könnyen PostgreSQL-re váltani
\item Teszteléskor valódi email-t küldene
\item Nem lehet mock objektumokat használni
\end{itemize}
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Megoldás Dependency Injection-nel (1/3)}
\begin{exampleblock}{Laza kapcsolás DI-val - Konstruktor}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
constructor(database, emailService, logger) {
// Függőségek kívülről érkeznek
this.database = database;
this.emailService = emailService;
this.logger = logger;
}
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Megoldás Dependency Injection-nel (2/3)}
\begin{exampleblock}{createUser metódus}
\small
\begin{lstlisting}[language=JavaScript]
async createUser(userData) {
this.logger.log('Creating user...');
const user = await this.database.save(userData);
await this.emailService.sendWelcomeEmail(
user.email);
return user;
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Megoldás Dependency Injection-nel (3/3)}
\begin{exampleblock}{Használat - függőségek kívülről}
\small
\begin{lstlisting}[language=JavaScript]
const database = new MySQLDatabase();
const emailService = new EmailService();
const logger = new Logger();
const userService = new UserService(
database,
emailService,
logger
);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}{DI előnyei}
\begin{block}{Miért jó a Dependency Injection?}
\footnotesize
\begin{enumerate}
\item \textbf{Tesztelhetőség}
\begin{itemize}
\item Mock és stub objektumok használata
\item Izolált unit tesztek
\end{itemize}
\item \textbf{Rugalmasság}
\begin{itemize}
\item Könnyen cserélhető implementációk
\item Különböző konfigurációk más környezetekben
\end{itemize}
\item \textbf{Karbantarthatóság}
\begin{itemize}
\item Egyértelmű függőségek
\item Single Responsibility Principle
\end{itemize}
\item \textbf{Újrafelhasználhatóság}
\begin{itemize}
\item Komponensek függetlenek
\item Könnyű más projektekben használni
\end{itemize}
\end{enumerate}
\end{block}
\end{frame}
\begin{frame}[fragile]{DI típusok - Constructor Injection (1/2)}
\begin{block}{Constructor Injection (Ajánlott)}
Függőségek átadása a konstruktorban
\end{block}
\begin{exampleblock}{Osztály konstruktor}
\small
\begin{lstlisting}[language=JavaScript]
class OrderService {
constructor(paymentGateway, inventoryService) {
this.paymentGateway = paymentGateway;
this.inventoryService = inventoryService;
}
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI típusok - Constructor Injection (2/2)}
\begin{exampleblock}{processOrder metódus}
\small
\begin{lstlisting}[language=JavaScript]
async processOrder(order) {
await this.inventoryService.reserveItems(
order.items);
await this.paymentGateway.charge(order.total);
return { success: true, orderId: order.id };
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Constructor Injection - Használat}
\begin{exampleblock}{Dependency injection}
\small
\begin{lstlisting}[language=JavaScript]
const paymentGateway = new StripeGateway();
const inventoryService = new InventoryService();
const orderService = new OrderService(
paymentGateway,
inventoryService
);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI típusok - Setter Injection (1/3)}
\begin{block}{Setter Injection}
Függőségek beállítása setter metódusokon keresztül
\end{block}
\begin{exampleblock}{Setter metódusok}
\small
\begin{lstlisting}[language=JavaScript]
class ReportService {
setDatabase(database) {
this.database = database;
}
setEmailService(emailService) {
this.emailService = emailService;
}
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI típusok - Setter Injection (2/3)}
\begin{exampleblock}{generateAndSendReport metódus}
\small
\begin{lstlisting}[language=JavaScript]
async generateAndSendReport() {
const data = await this.database.fetchData();
const report = this.createReport(data);
await this.emailService.send(report);
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI típusok - Setter Injection (3/3)}
\begin{exampleblock}{Használat}
\small
\begin{lstlisting}[language=JavaScript]
const reportService = new ReportService();
reportService.setDatabase(new MySQLDatabase());
reportService.setEmailService(new EmailService());
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}{Setter Injection - Hátrányok}
\begin{alertblock}{Hátrány}
Opcionális függőségek $\rightarrow$ könnyebb hibázni
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{DI típusok - Property Injection (1/2)}
\begin{block}{Property Injection}
Közvetlen property beállítás (ritkábban használt)
\end{block}
\begin{exampleblock}{Osztály definíció}
\small
\begin{lstlisting}[language=JavaScript]
class NotificationService {
async sendNotification(message) {
if (this.emailSender) {
await this.emailSender.send(message);
}
if (this.smsSender) {
await this.smsSender.send(message);
}
}
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI típusok - Property Injection (2/2)}
\begin{exampleblock}{Használat}
\small
\begin{lstlisting}[language=JavaScript]
const notificationService =
new NotificationService();
notificationService.emailSender =
new EmailSender();
notificationService.smsSender =
new SMSSender();
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}{Property Injection - Figyelmeztetés}
\begin{alertblock}{Figyelem}
Kevésbé explicit, nehezebb követni a függőségeket
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Dependency Injection TypeScript-tel (1/3)}
\begin{exampleblock}{Interfészek definiálása}
\small
\begin{lstlisting}[language=JavaScript]
interface IDatabase {
save(data: any): Promise<any>;
find(id: string): Promise<any>;
}
interface ILogger {
log(message: string): void;
error(message: string): void;
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Dependency Injection TypeScript-tel (2/4)}
\begin{exampleblock}{Service osztály konstruktor}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
constructor(
private database: IDatabase,
private logger: ILogger
) {}
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Dependency Injection TypeScript-tel (3/4)}
\begin{exampleblock}{createUser metódus}
\small
\begin{lstlisting}[language=JavaScript]
async createUser(userData: any) {
this.logger.log('Creating user');
return await this.database.save(userData);
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Dependency Injection TypeScript-tel (4/4)}
\begin{exampleblock}{Implementációk és használat}
\small
\begin{lstlisting}[language=JavaScript]
const db: IDatabase = new MongoDatabase();
const logger: ILogger = new ConsoleLogger();
const service = new UserService(db, logger);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI gyakorlatban - Tesztelés (1/4)}
\begin{exampleblock}{Eredeti service konstruktor}
\small
\begin{lstlisting}[language=JavaScript]
class AuthService {
constructor(database, jwtService) {
this.database = database;
this.jwtService = jwtService;
}
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI gyakorlatban - Tesztelés (2/4)}
\begin{exampleblock}{login metódus}
\small
\begin{lstlisting}[language=JavaScript]
async login(email, password) {
const user = await this.database.findByEmail(
email);
if (user && user.password === password) {
return this.jwtService.generateToken(user);
}
throw new Error('Invalid credentials');
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI gyakorlatban - Tesztelés (3/4)}
\begin{exampleblock}{Unit teszt - Mock objektumok (1/2)}
\small
\begin{lstlisting}[language=JavaScript]
test('login with valid credentials', async () => {
const mockDatabase = {
findByEmail: jest.fn().mockResolvedValue({
email: 'test@test.com',
password: 'pass123'
})
};
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI gyakorlatban - Tesztelés (4/4)}
\begin{exampleblock}{Unit teszt - Mock objektumok (2/2)}
\small
\begin{lstlisting}[language=JavaScript]
const mockJwtService = {
generateToken: jest.fn()
.mockReturnValue('token123')
};
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI gyakorlatban - Tesztelés (5/5)}
\begin{exampleblock}{Unit teszt - Érvényesítés}
\small
\begin{lstlisting}[language=JavaScript]
const authService = new AuthService(
mockDatabase, mockJwtService);
const token = await authService.login(
'test@test.com', 'pass123');
expect(token).toBe('token123');
expect(mockDatabase.findByEmail)
.toHaveBeenCalledWith('test@test.com');
});
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI gyakorlatban - Környezeti különbségek (1/3)}
\begin{exampleblock}{Development környezet}
\small
\begin{lstlisting}[language=JavaScript]
if (process.env.NODE_ENV === 'development') {
const database = new MockDatabase();
const emailService = new ConsoleEmailService();
const logger = new VerboseLogger();
app.locals.userService = new UserService(
database, emailService, logger
);
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI gyakorlatban - Környezeti különbségek (2/3)}
\begin{exampleblock}{Production környezet}
\small
\begin{lstlisting}[language=JavaScript]
if (process.env.NODE_ENV === 'production') {
const database = new PostgreSQLDatabase();
const emailService = new SendGridEmailService();
const logger = new CloudLogger();
app.locals.userService = new UserService(
database, emailService, logger
);
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI gyakorlatban - Környezeti különbségek (3/3)}
\begin{exampleblock}{Használat route-okban}
\small
\begin{lstlisting}[language=JavaScript]
app.post('/users', async (req, res) => {
const user = await app.locals.userService
.createUser(req.body);
res.json(user);
});
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Manuális DI - Factory Pattern (1/4)}
\begin{exampleblock}{Factory függvény - Alapok (1/2)}
\small
\begin{lstlisting}[language=JavaScript]
function createServices(config) {
// Alapvető szolgáltatások
const logger = new Logger(config.logLevel);
const database = new Database(config.dbUrl);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Manuális DI - Factory Pattern (2/4)}
\begin{exampleblock}{Factory függvény - Alapok (2/2)}
\small
\begin{lstlisting}[language=JavaScript]
// Összetett szolgáltatások
const userRepository =
new UserRepository(database);
const emailService =
new EmailService(config.emailConfig);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Manuális DI - Factory Pattern (3/5)}
\begin{exampleblock}{Service-ek létrehozása (1/2)}
\small
\begin{lstlisting}[language=JavaScript]
const userService = new UserService(
userRepository, emailService, logger
);
const authService = new AuthService(
userRepository, config.jwtSecret, logger
);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Manuális DI - Factory Pattern (4/5)}
\begin{exampleblock}{Service-ek létrehozása (2/2)}
\small
\begin{lstlisting}[language=JavaScript]
return {
userService, authService,
logger, database
};
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Manuális DI - Factory Pattern (5/5)}
\begin{exampleblock}{Használat}
\small
\begin{lstlisting}[language=JavaScript]
const config = loadConfig();
const services = createServices(config);
export default services;
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Service Locator Pattern (1/4)}
\begin{exampleblock}{Service container konstruktor}
\small
\begin{lstlisting}[language=JavaScript]
class ServiceContainer {
constructor() {
this.services = new Map();
}
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Service Locator Pattern (2/4)}
\begin{exampleblock}{register és get metódusok}
\small
\begin{lstlisting}[language=JavaScript]
register(name, instance) {
this.services.set(name, instance);
}
get(name) {
if (!this.services.has(name)) {
throw new Error(`Service ${name} not found`);
}
return this.services.get(name);
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Service Locator Pattern (3/4)}
\begin{exampleblock}{Service-ek regisztrálása (1/2)}
\small
\begin{lstlisting}[language=JavaScript]
const container = new ServiceContainer();
container.register('database',
new MySQLDatabase());
container.register('logger', new Logger());
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Service Locator Pattern (4/4)}
\begin{exampleblock}{Service-ek regisztrálása (2/2)}
\small
\begin{lstlisting}[language=JavaScript]
container.register('userService',
new UserService(
container.get('database'),
container.get('logger')
)
);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Service Locator Pattern (5/5)}
\begin{exampleblock}{Használat}
\small
\begin{lstlisting}[language=JavaScript]
const userService = container.get('userService');
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI az Express middleware-ekben (1/4)}
\begin{exampleblock}{Service-ek létrehozása}
\small
\begin{lstlisting}[language=JavaScript]
const database = new Database();
const userService = new UserService(database);
const authService = new AuthService(database);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI az Express middleware-ekben (2/6)}
\begin{exampleblock}{Middleware factory - Setup}
\small
\begin{lstlisting}[language=JavaScript]
function createAuthMiddleware(authService) {
return async (req, res, next) => {
const token = req.headers.authorization
?.split(' ')[1];
if (!token) {
return res.status(401)
.json({ error: 'No token' });
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI az Express middleware-ekben (3/6)}
\begin{exampleblock}{Middleware factory - Verifikáció}
\small
\begin{lstlisting}[language=JavaScript]
try {
req.user = await authService.verifyToken(
token);
next();
} catch (error) {
res.status(401)
.json({ error: 'Invalid token' });
}
};
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI az Express middleware-ekben (4/6)}
\begin{exampleblock}{Route handler factory}
\small
\begin{lstlisting}[language=JavaScript]
function createUserController(userService) {
return {
async createUser(req, res) {
const user = await userService.createUser(
req.body);
res.json(user);
}
};
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI az Express middleware-ekben (5/6)}
\begin{exampleblock}{Setup (1/2)}
\small
\begin{lstlisting}[language=JavaScript]
const authMiddleware =
createAuthMiddleware(authService);
const userController =
createUserController(userService);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{DI az Express middleware-ekben (6/6)}
\begin{exampleblock}{Setup (2/2)}
\small
\begin{lstlisting}[language=JavaScript]
app.post('/users',
authMiddleware,
userController.createUser
);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}{SOLID elvek és DI}
\begin{block}{DI kapcsolata a SOLID elvekkel}
\begin{description}
\item[S - Single Responsibility] Osztályok egy dolgot csinálnak, nem hozzák létre saját függőségeiket
\item[O - Open/Closed] Új implementációk hozzáadhatók a meglévő kód módosítása nélkül
\item[L - Liskov Substitution] Interfészek lehetővé teszik a helyettesítést
\item[I - Interface Segregation] Kis, specifikus interfészek használata
\item[D - Dependency Inversion] \textbf{Függőség absztrakciókra, nem konkrét implementációkra}
\end{description}
\end{block}
\begin{alertblock}{Dependency Inversion Principle}
\begin{itemize}
\item High-level modulok nem függnek low-level moduloktól
\item Mindkettő absztrakcióktól (interfészektől) függ
\item DI ezt a célt szolgálja
\end{itemize}
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Best Practices}
\begin{block}{Ajánlások}
\footnotesize
\begin{enumerate}
\item \textbf{Constructor Injection előnyben részesítése}
\begin{itemize}
\item Kötelező függőségek explicit módon láthatók
\item Immutábilis objektumok
\end{itemize}
\item \textbf{Interfészekre programozás}
\begin{itemize}
\item TypeScript interfészek használata
\item Laza kapcsolás
\end{itemize}
\item \textbf{Ne használj Service Locator az üzleti logikában}
\begin{itemize}
\item Rejtett függőségek
\item Csak konfigurációs szinten
\end{itemize}
\item \textbf{Kerüld a túl sok függőséget}
\begin{itemize}
\item Ha sok függőség van, valószínűleg túl sok felelőssége van az osztálynak
\item Refaktorálás szükséges
\end{itemize}
\end{enumerate}
\end{block}
\end{frame}
\begin{frame}[fragile]{Antipattern - Service Locator visszaélés (1/3)}
\begin{alertblock}{Rossz gyakorlat - Konstruktor}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
constructor(container) {
// Teljes container injektálása
this.container = container;
}
}
\end{lstlisting}
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Antipattern - Service Locator visszaélés (2/3)}
\begin{alertblock}{Rossz gyakorlat - createUser metódus}
\small
\begin{lstlisting}[language=JavaScript]
async createUser(userData) {
// Függőségek rejtettek!
const db = this.container.get('database');
const email = this.container.get('emailService');
const logger = this.container.get('logger');
logger.log('Creating user');
const user = await db.save(userData);
await email.sendWelcome(user.email);
return user;
}
\end{lstlisting}
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Antipattern - Service Locator visszaélés (3/3)}
\begin{exampleblock}{Jó gyakorlat}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
constructor(database, emailService, logger) {
this.database = database;
this.emailService = emailService;
this.logger = logger;
}
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}{Összefoglalás (1/2)}
\begin{block}{Dependency Injection lényege}
\begin{itemize}
\item Függőségek kívülről történő átadása
\item Laza kapcsolás objektumok között
\item Tesztelhetőség növelése
\item SOLID elvek támogatása
\end{itemize}
\end{block}
\begin{block}{Implementációs módszerek}
\begin{itemize}
\item Constructor Injection (preferált)
\item Setter Injection (opcionális függőségekhez)
\item Manuális DI factory pattern-nel
\item Service Container (konfigurációs szinten)
\end{itemize}
\end{block}
\end{frame}
\begin{frame}{Összefoglalás (2/2)}
\begin{block}{Előnyök}
\begin{itemize}
\item Könnyű tesztelés mock objektumokkal
\item Rugalmas, cserélhető implementációk
\item Tisztább, karbantarthatóbb kód
\item Explicit függőségek
\end{itemize}
\end{block}
\end{frame}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,23 @@
\documentclass[usenames,dvipsnames,aspectratio=169]{beamer}
\usepackage{../common/webfejl}
% Automatikus frame törés engedélyezése túl hosszú tartalomnál
\setbeamertemplate{frametitle continuation}[from second][\insertcontinuationcountroman]
\title[Backend fejlesztés - CORS, DI, Email]{Webtechnológia és webalkalmazás-fejlesztés}
\subtitle{CORS, Dependency Injection, Email Sending}
\begin{document}
\begin{frame}[plain]
\titlepage
\logoalul
\end{frame}
\input{cors.tex}
\input{dependency_injection.tex}
\input{dic.tex}
\input{email_sending.tex}
\input{email_templating.tex}
\end{document}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,658 @@
\section{Templating}
\begin{frame}{Miért használjunk email template-eket? (1/2)}
\begin{block}{Problémák template nélkül}
\begin{itemize}
\item HTML string-ek a kódban $\rightarrow$ nehéz karbantartani
\item Duplikált kód különböző email típusokhoz
\item Nehéz változtatni a design-t
\item Frontend és backend keveredik
\item Nincs újrafelhasználhatóság
\end{itemize}
\end{block}
\end{frame}
\begin{frame}{Miért használjunk email template-eket? (2/2)}
\begin{exampleblock}{Template-ekkel}
\begin{itemize}
\item Szeparált template fájlok
\item Dinamikus adatok injektálása
\item Újrafelhasználható komponensek (header, footer)
\item Könnyű módosítás és tesztelés
\item Designer-ek is módosíthatják
\item Verziókövetés
\end{itemize}
\end{exampleblock}
\end{frame}
\begin{frame}{Template Engine-ek}
\begin{block}{Népszerű template engine-ek Node.js-hez}
\begin{description}
\item[Handlebars] Logika-mentes, egyszerű szintaxis, blokk helper-ek
\item[EJS] Embedded JavaScript, ismerős szintaxis
\item[Pug (Jade)] Tömör, whitespace-based szintaxis
\item[Nunjucks] Jinja2-szerű, gazdag funkciók
\item[Mustache] Minimális, logika-mentes
\end{description}
\end{block}
\begin{block}{Email-specifikus eszközök}
\begin{description}
\item[MJML] Responsive email framework - JSON/XML-szerű szintaxis
\item[Foundation for Emails] Responsive email framework
\item[Email-templates] Nodemailer integráció többféle engine-hez
\end{description}
\end{block}
\end{frame}
\begin{frame}[fragile]{Handlebars alapok}
\begin{block}{Telepítés}
\small
\begin{lstlisting}[language=bash]
npm install handlebars
\end{lstlisting}
\end{block}
\begin{exampleblock}{Template szintaxis (1/2)}
\small
\begin{lstlisting}[language=HTML]
<!DOCTYPE html>
<html>
<head>
<title>{{subject}}</title>
</head>
<body>
<h1>Hello {{username}}!</h1>
<p>Your email is: {{email}}</p>
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Handlebars alapok (2/3)}
\begin{exampleblock}{Template szintaxis - Conditionals}
\small
\begin{lstlisting}[language=HTML]
{{#if isPremium}}
<p>Thanks for being a premium member!</p>
{{else}}
<p>Upgrade to premium for more features.</p>
{{/if}}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Handlebars alapok (3/3)}
\begin{exampleblock}{Template szintaxis - Loops}
\small
\begin{lstlisting}[language=HTML]
<ul>
{{#each items}}
<li>{{this.name}} - ${{this.price}}</li>
{{/each}}
</ul>
</body>
</html>
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Handlebars használata Node.js-ben (1/3)}
\begin{exampleblock}{Setup és template beolvasás}
\small
\begin{lstlisting}[language=JavaScript]
const handlebars = require('handlebars');
const fs = require('fs').promises;
async function renderTemplate(templateName, data) {
// Template fájl beolvasása
const templatePath = `./templates/${templateName}.hbs`;
const templateSource = await fs.readFile(templatePath, 'utf-8');
// Template compile
const template = handlebars.compile(templateSource);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Handlebars használata Node.js-ben (2/3)}
\begin{exampleblock}{Render adatokkal}
\small
\begin{lstlisting}[language=JavaScript]
async function renderTemplate(templateName, data) {
// ... template beolvasás és compile
// Render adatokkal
const html = template(data);
return html;
}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Handlebars használata Node.js-ben (3/4)}
\begin{exampleblock}{Használat példa - Data}
\small
\begin{lstlisting}[language=JavaScript]
const data = {
username: 'John Doe',
email: 'john@example.com',
isPremium: true,
items: [
{ name: 'Product 1', price: 29.99 },
{ name: 'Product 2', price: 49.99 }
]
};
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Handlebars használata Node.js-ben (4/4)}
\begin{exampleblock}{Használat példa - Render}
\small
\begin{lstlisting}[language=JavaScript]
const html = await renderTemplate('welcome', data);
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Handlebars custom helper-ek (1/3)}
\begin{exampleblock}{Dátum helper}
\small
\begin{lstlisting}[language=JavaScript]
const handlebars = require('handlebars');
// Egyszerű helper - dátum formázás
handlebars.registerHelper('formatDate', (date) => {
return new Date(date).toLocaleDateString('hu-HU');});
\end{lstlisting}
\end{exampleblock}
\begin{exampleblock}{Currency helper}
\small
\begin{lstlisting}[language=JavaScript]
// Helper paraméterekkel
handlebars.registerHelper('formatCurrency',
(amount, currency = 'USD') => {
return new Intl.NumberFormat('en-US', {
style: 'currency', currency: currency}).format(amount);});
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Handlebars custom helper-ek (2/3)}
\begin{exampleblock}{Block helper - feltételes logika}
\small
\begin{lstlisting}[language=JavaScript]
// Block helper - feltételes logika
handlebars.registerHelper('ifEquals',
function(arg1, arg2, options) {
return (arg1 === arg2)
? options.fn(this)
: options.inverse(this);
});
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Handlebars custom helper-ek (3/3)}
\begin{exampleblock}{Template használat}
\small
\begin{lstlisting}[language=HTML]
<p>Order date: {{formatDate orderDate}}</p>
<p>Total: {{formatCurrency total 'EUR'}}</p>
{{#ifEquals status 'completed'}}
<p>Order is completed!</p>
{{else}}
<p>Order is pending.</p>
{{/ifEquals}}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Handlebars partials - újrafelhasználható részek (1/3)}
\begin{exampleblock}{Setup}
\small
\begin{lstlisting}[language=JavaScript]
const handlebars = require('handlebars');
const fs = require('fs').promises;
// Partial-ok betöltése
async function registerPartials() {
const header = await fs.readFile(
'./templates/partials/header.hbs', 'utf-8');
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Handlebars partials - újrafelhasználható részek (2/3)}
\begin{exampleblock}{Partial fájlok beolvasása}
\small
\begin{lstlisting}[language=JavaScript]
const footer = await fs.readFile(
'./templates/partials/footer.hbs', 'utf-8');
const button = await fs.readFile(
'./templates/partials/button.hbs', 'utf-8');
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Handlebars partials - újrafelhasználható részek (3/3)}
\begin{exampleblock}{Partial regisztráció}
\small
\begin{lstlisting}[language=JavaScript]
// Partials regisztrálása
handlebars.registerPartial('header', header);
handlebars.registerPartial('footer', footer);
handlebars.registerPartial('button', button);
}
await registerPartials();
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Partial példák (1/2)}
\begin{columns}[T]
\begin{column}{0.48\textwidth}
\textbf{header.hbs}
\begin{lstlisting}[language=HTML]
<div style="background: #333;
color: white;
padding: 20px;">
<img src="{{logoUrl}}"
alt="Logo"/>
<h1>{{companyName}}</h1>
</div>
\end{lstlisting}
\end{column}
\begin{column}{0.48\textwidth}
\textbf{footer.hbs}
\begin{lstlisting}[language=HTML]
<div style="text-align: center;">
<p>© {{year}}</p>
</div>
\end{lstlisting}
\end{column}
\end{columns}
\end{frame}
\begin{frame}[fragile]{Partial példák (2/2)}
\begin{exampleblock}{main.hbs - Partials használata}
\small
\begin{lstlisting}[language=HTML]
<!DOCTYPE html>
<html>
<body>
{{> header}}
<div class="content">
{{body}}
</div>
{{> footer}}
</body>
</html>
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Email-templates csomag - All-in-one megoldás}
\begin{block}{Telep\u00edt\u00e9s}
\small
\begin{lstlisting}[language=bash]
npm install email-templates
\end{lstlisting}
\end{block}
\begin{block}{Jellemz\u0151k}
\begin{itemize}
\item Automatikus HTML \u00e9s text verzi\u00f3 gener\u00e1l\u00e1s
\item CSS inlining (email client compatibility)
\item Template engine v\u00e1laszt\u00e9k (Pug, Handlebars, EJS, etc.)
\item Preview server fejleszt\u00e9shez
\item Nodemailer integr\u00e1ci\u00f3
\end{itemize}
\end{block}
\end{frame}
\begin{frame}[fragile]{Email-templates haszn\u00e1lata (1/3)}
\begin{exampleblock}{Transporter setup}
\small
\begin{lstlisting}[language=JavaScript]
const Email = require('email-templates');
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
});\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Email-templates haszn\u00e1lata (2/3)}
\begin{exampleblock}{Email konfigur\u00e1ci\u00f3}
\small
\begin{lstlisting}[language=JavaScript]
const email = new Email({
message: { from: 'noreply@example.com' },
transport: transporter,
views: {
root: './emails', // Template könyvtár
options: {
extension: 'hbs' // Handlebars
}
},
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Email-templates haszn\u00e1lata (3/3)}
\begin{exampleblock}{CSS inlining config}
\small
\begin{lstlisting}[language=JavaScript]
// ... folytat\u00e1s
juice: true, // CSS inlining
juiceResources: {
preserveImportant: true,
webResources: {
relativeTo: './emails'
}
}
});
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Email-templates - Email küldés (1/2)}
\begin{exampleblock}{Welcome email példa}
\small
\begin{lstlisting}[language=JavaScript]
// Email küldés template-tel
await email.send({
template: 'welcome', // ./emails/welcome könyvtár
message: { to: 'user@example.com' },
locals: {
username: 'John Doe',
verifyUrl: 'https://example.com/verify?token=abc123',
year: new Date().getFullYear()
}
});
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Email-templates - Email küldés (2/2)}
\begin{exampleblock}{Order confirmation példa}
\small
\begin{lstlisting}[language=JavaScript]
// Több template eset
await email.send({
template: 'order-confirmation',
message: { to: order.customerEmail },
locals: {
orderNumber: order.id, items: order.items,
total: order.total, shippingAddress: order.address
}
});
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Template könyvtár struktúra (1/2)}
\begin{exampleblock}{Fájl struktúra - Layouts és Partials}
\small
\begin{lstlisting}[language=bash]
emails/
├── _layouts/
│ └── default.hbs # Alap layout
├── _partials/
│ ├── header.hbs
│ ├── footer.hbs
│ └── button.hbs
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Template könyvtár struktúra (2/3)}
\begin{exampleblock}{Fájl struktúra - Welcome template}
\small
\begin{lstlisting}[language=bash]
├── welcome/
│ ├── html.hbs # HTML verzió
│ ├── text.hbs # Plain text
│ ├── subject.hbs # Subject
│ └── style.css # CSS (inline-olva lesz)
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Template könyvtár struktúra (3/3)}
\begin{exampleblock}{Fájl struktúra - További templates}
\small
\begin{lstlisting}[language=bash]
├── password-reset/
│ ├── html.hbs
│ ├── text.hbs
│ └── subject.hbs
└── order-confirmation/
├── html.hbs
├── text.hbs
└── subject.hbs
\end{lstlisting}
\end{exampleblock}
\begin{alertblock}{Fontos}
Minden template-hez készíts \texttt{text.hbs} verziót is (accessibility)!
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Welcome template példa - HTML (1/3)}
\begin{exampleblock}{emails/welcome/html.hbs - Head}
\small
\begin{lstlisting}[language=HTML]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
</head>
<body>
{{> header companyName="MyApp" logoUrl="/logo.png"}}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Welcome template példa - HTML (2/3)}
\begin{exampleblock}{emails/welcome/html.hbs - Content}
\small
\begin{lstlisting}[language=HTML]
<div class="container">
<h1>Welcome, {{username}}!</h1>
<p>Thank you for joining MyApp.
We're excited to have you on board!</p>
<p>To get started, please verify your email address
by clicking the button below:</p>
{{> button text="Verify Email" url=verifyUrl color="#4CAF50"}}
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Welcome template példa - HTML (3/3)}
\begin{exampleblock}{emails/welcome/html.hbs - Footer}
\small
\begin{lstlisting}[language=HTML]
<p>If you didn't create an account,
please ignore this email.</p>
</div>
{{> footer year=year}}
</body>
</html>
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Welcome template példa - Text (1/2)}
\begin{exampleblock}{emails/welcome/text.hbs - Content}
\small
\begin{lstlisting}[language=plaintext]
Welcome, {{username}}!
Thank you for joining MyApp.
We're excited to have you on board!
To get started, please verify your email address
by visiting:
{{verifyUrl}}
If you didn't create an account,
please ignore this email.
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Welcome template példa - Text (2/2)}
\begin{exampleblock}{emails/welcome/text.hbs - Signature}
\small
\begin{lstlisting}[language=plaintext]
Best regards,
The MyApp Team
© {{year}} MyApp. All rights reserved.
\end{lstlisting}
\end{exampleblock}
\begin{exampleblock}{emails/welcome/subject.hbs}
\small
\begin{lstlisting}[language=plaintext]
Welcome to MyApp, {{username}}!
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}{Template subject - Megjegyzés}
\begin{alertblock}{Megjegyzés}
A \texttt{subject.hbs} is lehet dinamikus template!
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Reusable button partial (1/2)}
\begin{exampleblock}{emails/\_partials/button.hbs - Table structure}
\small
\begin{lstlisting}[language=HTML]
<table role="presentation" cellspacing="0" cellpadding="0"
border="0" align="center" style="margin: 20px 0;">
<tr>
<td style="border-radius: 4px; background: {{color}};">
<a href="{{url}}"
style="border: none;
color: white;
padding: 12px 24px;
text-decoration: none;
display: inline-block;
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]{Reusable button partial (2/2)}
\begin{exampleblock}{emails/\_partials/button.hbs - Link styling}
\small
\begin{lstlisting}[language=HTML]
style="...
font-size: 16px;
font-weight: bold;">
{{text}}
</a>
</td>
</tr>
</table>
\end{lstlisting}
\end{exampleblock}
\begin{alertblock}{Miért table layout?}
Email client-ek (Outlook, Gmail) rosszul támogatják a modern CSS-t.
Table-based layout a legkompatibilisebb megoldás.
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{CSS inlining}
\begin{block}{Miért kell CSS inlining?}
\begin{itemize}
\item Gmail és sok email client eltávolítja a \texttt{<style>} tag-et
\item Biztonságos megoldás: inline style attribute-ok
\item Automatikus: \texttt{juice} library végzi
\end{itemize}
\end{block}
\begin{columns}[T]
\begin{column}{0.48\textwidth}
\textbf{Előtte (style.css)}
\begin{lstlisting}[language=CSS]
.button {
background: #4CAF50;
color: white;
padding: 12px 24px;
}
\end{lstlisting}
\end{column}
\begin{column}{0.48\textwidth}
\textbf{Utána (inline)}
\begin{lstlisting}[language=HTML]
<a style="background: #4CAF50;
color: white;
padding: 12px 24px;">
Button
</a>
\end{lstlisting}
\end{column}
\end{columns}
\end{frame}
\begin{frame}[fragile]{CSS inlining - Fontos beállítások}
\begin{exampleblock}{Email-templates automatikusan végzi}
\small
\begin{lstlisting}[language=JavaScript]
const email = new Email({
// ...
juice: true, // CSS inlining engedélyezése
juiceResources: {
preserveImportant: true
}
});
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}{Összefoglalás (1/2)}
\begin{block}{Email templating lényege}
\begin{itemize}
\item Szeparált, karbantartható email template-ek
\item Dinamikus adatok injektálása
\item Újrafelhasználható komponensek (partials)
\item HTML és plain text verziók
\end{itemize}
\end{block}
\begin{block}{Eszközök}
\begin{itemize}
\item Template engine-ek: Handlebars, EJS, Pug
\item Email-specific: MJML, Foundation for Emails
\item Node.js library: email-templates csomag
\item CSS inlining: juice library
\end{itemize}
\end{block}
\end{frame}
\begin{frame}{Összefoglalás (2/2)}
\begin{block}{Kulcsfontosságú szempontok}
\begin{itemize}
\item Email client kompatibilitás (table layout, inline CSS)
\item Responsive design mobil eszközökre
\item Tesztelés és preview development-ben
\item Clean architecture: EmailService osztály
\end{itemize}
\end{block}
\end{frame}