Dokumentace směrování a middlewaru
Kompletní průvodce směrováním a middlewarem v CatCMS s využitím frameworku Hono, optimalizovaného pro Cloudflare Workers.[object Object]
Přehled
CatCMS používáHono, lehký webový framework optimalizovaný pro Cloudflare Workers. Pipeline middlewaru zpracovává každý požadavek skrze sérii vrstev předtím, než se dostane k obslužným rutinám (route handlers).
Klíčové koncepty
- Middleware - Funkce, které se spouštějí před obslužnými rutinami (route handlers) ke zpracování požadavků
- Pořadí middlewaru - Na pořadí záleží - middleware se spouští v pořadí, v jakém je registrován
- Ochrana tras (routes) - Middleware může chránit trasy (routes) na základě autentizace, rolí a oprávnění
- Objekt kontextu - Middleware může nastavit hodnoty v kontextu (
c.set()) pro pozdější použití v řetězci
Authentication
JWT-based auth with KV caching
Authorization
Fine-grained permission system
Bootstrap
System initialization on startup
Logging
Request/response/security logging
Performance
Caching and security headers
Plugin Middleware
Route protection by plugin status
Pipeline middlewaru
Pipeline middlewaru se pro každý požadavek spouští v následujícím pořadí:
// File: /src/index.ts
// 1. Bootstrap - System initialization (runs once per worker)
app.use('\*', bootstrapMiddleware())
// 2. Logging - Request/response logging
app.use('_', loggingMiddleware())
app.use('_', securityLoggingMiddleware())
app.use('\*', performanceLoggingMiddleware(1000)) // Log slow requests
// 3. Standard Middleware
app.use('_', logger()) // Hono's built-in logger
app.use('_', cors()) // CORS headers
app.use('_', securityHeaders()) // Security headers
app.use('/api/_', prettyJSON()) // JSON formatting for API routes
// 4. Route-specific middleware (applied to specific paths)
app.use('/admin/_', requireAuth())
app.use('/admin/_', requireRole(['admin', 'editor']))
app.use('/admin/\*', cacheHeaders(60))
Middleware Configuration
Pořadí spouštění middlewaru
Request
↓
Bootstrap Middleware → Check/run system initialization
↓
Logging Middleware → Log request start
↓
Security Logging → Check for suspicious patterns
↓
Performance Logging → Track request timing
↓
Standard Middleware → CORS, security headers, etc.
↓
Route-specific Middleware → Auth, roles, permissions
↓
Route Handler → Execute business logic
↓
Logging Middleware → Log request completion
↓
Response
Autentizační middleware
Autentizační systém používá tokeny JWT uložené v cookies typu HTTP-only.
Správce autentizace
import { AuthManager } from '../middleware/auth'
// Generate JWT token
const token = await AuthManager.generateToken(
userId,
email,
role
)
// Verify JWT token
const payload = await AuthManager.verifyToken(token)
// Returns: { userId, email, role, exp, iat } or null
// Hash password
const hash = await AuthManager.hashPassword(password)
// Verify password
const isValid = await AuthManager.verifyPassword(password, hash)Auth Manager
Middleware requireAuth
Vyžaduje platnou autentizaci pro přístup k trase (route).
import { requireAuth } from '../middleware/auth'
// Protect a route
app.get('/protected', requireAuth(), async (c) => {
const user = c.get('user')
// user contains: { userId, email, role, exp, iat }
return c.json({ message: 'Welcome!', user })
})
Require Auth
Jak to funguje:
- Zkontroluje token v
Authorizationhlavičce (Bearer token) - Pokud není v hlavičce, použije
auth_tokencookie - Ověří token v KV cache (TTL 5 minut)
- Pokud není v cache, provede ověření JWT
- Nastaví
userobjekt v kontextu pro pozdější použití - Vrátí chybu 401 nebo přesměruje na přihlášení, pokud je neplatný
Middleware requireRole
Vyžaduje specifickou roli (nebo role) pro přístup k trase (route).
import { requireRole } from '../middleware/auth'
// Single role
app.get('/admin-only',
requireAuth(),
requireRole('admin'),
async (c) => {
return c.json({ message: 'Admin area' })
}
)
// Multiple roles (any of them)
app.get('/content-edit',
requireAuth(),
requireRole(['admin', 'editor']),
async (c) => {
return c.json({ message: 'Content editing area' })
}
)Role-Based Access
Hierarchie rolí:
admin- Plný přístup do systémueditor- Správa obsahu a publikováníviewer- Přístup pouze pro čtení
Middleware optionalAuth
Umožňuje autentizovaný i neautentizovaný přístup, ale pokud je uživatel autentizován, doplní jeho data.
import { optionalAuth } from '../middleware/auth'
// Public API with optional auth
app.get('/content', optionalAuth(), async (c) => {
const user = c.get('user')
const db = c.env.DB
// Show different content based on authentication
const query = user
? 'SELECT * FROM content WHERE status IN (?, ?, ?)'
: 'SELECT * FROM content WHERE status = ?'
const params = user
? ['published', 'draft', 'scheduled']
: ['published']
const { results } = await db.prepare(query).bind(...params).all()
return c.json({ data: results, authenticated: !!user })
})
Optional Auth
Autorizace a oprávnění
Jemně granulovaný systém oprávnění pro kontrolu přístupu ke specifickým zdrojům.
Správce oprávnění
import { PermissionManager } from '../middleware/permissions'
// Get user permissions
const permissions = await PermissionManager.getUserPermissions(db, userId)
// Returns: { userId, role, permissions: string[], teamPermissions: {} }
// Check single permission
const hasPermission = await PermissionManager.hasPermission(
db,
userId,
'content.edit'
)
// Check multiple permissions
const permissionMap = await PermissionManager.checkMultiplePermissions(
db,
userId,
['content.edit', 'content.publish', 'users.create']
)
// Returns: { 'content.edit': true, 'content.publish': false, ... }
// Clear user permission cache
PermissionManager.clearUserCache(userId)
// Clear all permission cache
PermissionManager.clearAllCache()Permission Manager
Middleware requirePermission
Vyžaduje specifické oprávnění pro přístup k trase (route).
import { requirePermission } from '../middleware/permissions'
// Single permission
app.post('/content',
requireAuth(),
requirePermission('content.create'),
async (c) => {
// User has content.create permission
return c.json({ message: 'Content created' })
}
)
// Permission with team context
app.get('/teams/:teamId/settings',
requireAuth(),
requirePermission('team.settings', 'teamId'),
async (c) => {
const teamId = c.req.param('teamId')
// User has team.settings permission for this specific team
return c.json({ message: `Team ${teamId} settings` })
}
)
Permission-Based Access
Konvence pro pojmenování oprávnění
Oprávnění se řídí vzorem: resource.action
Běžná oprávnění:
content.create- Vytvářet obsahcontent.edit- Upravovat obsahcontent.publish- Publikovat obsahcontent.delete- Mazat obsahusers.create- Vytvářet uživateleusers.edit- Upravovat uživateleusers.delete- Mazat uživatelemedia.upload- Nahrávat mediální souborymedia.delete- Mazat mediální soubory
Bootstrap middleware
Zajišťuje inicializaci systému při spuštění workeru.
Účel
Bootstrap middleware se spouští jednou pro každou instanci Cloudflare Workeru za účelem:
- Spustit čekající databázové migrace
- Synchronizovat konfigurace kolekcí
- Inicializovat jádrové pluginy
- Nainstalovat demo pluginy (pouze ve vývojovém prostředí)
import { bootstrapMiddleware, resetBootstrap } from '../middleware/bootstrap'
// Apply to all routes
app.use('*', bootstrapMiddleware())
// Reset bootstrap flag (useful for testing)
resetBootstrap()Bootstrap Middleware
Implementace
let bootstrapComplete = false
export function bootstrapMiddleware() {
return async (c: Context, next: Next) => {
// Skip if already bootstrapped
if (bootstrapComplete) {
return next()
}
// Skip bootstrap for static assets
const path = c.req.path
if (
path.startsWith('/images/') ||
path.startsWith('/assets/') ||
path === '/health' ||
path.endsWith('.js') ||
path.endsWith('.css')
) {
return next()
}
try {
console.log('[Bootstrap] Starting system initialization...')
// 1. Run database migrations
const migrationService = new MigrationService(c.env.DB)
await migrationService.runPendingMigrations()
// 2. Sync collection configurations
await syncCollections(c.env.DB)
// 3. Bootstrap core plugins
const bootstrapService = new PluginBootstrapService(c.env.DB)
const needsBootstrap = await bootstrapService.isBootstrapNeeded()
if (needsBootstrap) {
await bootstrapService.bootstrapCorePlugins()
await bootstrapService.installDemoPlugins()
}
bootstrapComplete = true
console.log('[Bootstrap] System initialization completed')
} catch (error) {
console.error('[Bootstrap] Error during initialization:', error)
// Continue even if bootstrap fails
}
return next()
}
}
Bootstrap Process
Logovací middleware
Komplexní logování požadavků/odpovědí s bezpečnostním monitorováním.
Standardní logovací middleware
Loguje všechny HTTP požadavky a odpovědi.
import { loggingMiddleware } from '../middleware/logging'
app.use('*', loggingMiddleware())Logging
Co loguje:
- Metodu požadavku, URL a hlavičky
- Stavový kód odpovědi
- Dobu trvání požadavku
- ID uživatele (pokud je autentizován)
- IP adresu a user agent
- ID požadavku (vygenerované UUID)
Bezpečnostní logovací middleware
Monitoruje podezřelou aktivitu a bezpečnostní události.
import { securityLoggingMiddleware } from '../middleware/logging'
app.use('\*', securityLoggingMiddleware())
Security Logging
Co monitoruje:
- Podezřelé vzory požadavků (pokusy o SQL injection, XSS)
- Neúspěšná přihlášení
- Přístupy do administrace
- Pokusy o neoprávněný přístup
Detekovány podezřelé vzory:
const suspiciousPatterns = [
/script[^>]*>/i, // XSS attempts
/javascript:/i, // JavaScript protocol
/on\w+\s*=/i, // Event handlers
/\.\.\/\.\.\//, // Directory traversal
/\/etc\/passwd/i, // System file access
/union\s+select/i, // SQL injection
/drop\s+table/i // SQL injection
]
Middleware pro logování výkonu
Sleduje pomalé požadavky pro monitorování výkonu.
import { performanceLoggingMiddleware } from '../middleware/logging'
// Log requests slower than 1000ms (1 second)
app.use('\*', performanceLoggingMiddleware(1000))
// Log requests slower than 500ms for critical API
app.use('/api/critical/\*', performanceLoggingMiddleware(500))
Performance Logging
Middleware pro výkon
Middleware pro cachování, kompresi a bezpečnostní hlavičky.
Middleware pro hlavičky cache
Přidává do odpovědí hlavičky cache-control.
import { cacheHeaders } from '../middleware/performance'
// Cache for 60 seconds
app.use('/admin/*', cacheHeaders(60))
// Cache for 5 minutes
app.use('/api/static/*', cacheHeaders(300))Cache Headers
Jak to funguje:
- Cachuje pouze úspěšné HTML odpovědi (status 200)
- Nastavuje
Cache-Control: private, max-age={maxAge} - Privátní cachování zabraňuje CDN cachovat stránky pro přihlášené uživatele
Middleware pro bezpečnostní hlavičky
Přidává bezpečnostní hlavičky do všech odpovědí.
import { securityHeaders } from '../middleware/performance'
app.use('\*', securityHeaders())
Security Headers
Přidané hlavičky:
'X-Content-Type-Options': 'nosniff'
'X-Frame-Options': 'SAMEORIGIN'
'X-XSS-Protection': '1; mode=block'
Middleware pro pluginy
Řídí přístup k trasám (routes) pluginů na základě stavu jejich aktivace.
Middleware requireActivePlugin
Zajišťuje, že je plugin aktivní, než povolí přístup k jeho trasám (routes).
import { requireActivePlugin } from '../middleware/plugin-middleware'
// Protect FAQ plugin routes
app.use('/admin/faq/\*', requireActivePlugin('faq'))
app.route('/admin/faq', adminFAQRoutes)
// Protect workflow plugin routes
app.use('/admin/workflow/\*', requireActivePlugin('workflow'))
app.route('/admin/workflow', createWorkflowAdminRoutes())
// Protect cache plugin routes
app.use('/admin/cache/\*', requireActivePlugin('cache'))
app.route('/admin/cache', cacheRoutes)
Plugin Middleware
Jak to funguje:
- Dotazuje se databáze na stav pluginu
- Vrátí chybu 404 s uživatelsky přívětivou zprávou, pokud plugin není aktivní
- Povolí pokračování požadavku, pokud je plugin aktivní
- V případě selhání dotazu do databáze povolí přístup (fails open)
Struktura tras (routes)
CatCMS organizuje trasy (routes) do logických modulů.
Veřejné trasy (routes)
Není vyžadována autentizace.
// Authentication pages
GET /auth/login # Login page
POST /auth/login # Login form submission
GET /auth/register # Registration page
POST /auth/register # Registration form submission
GET /auth/logout # Logout
POST /auth/logout # Logout API
// Public API
GET /api/ # OpenAPI specification
GET /api/health # Health check
GET /api/collections # List collections
GET /api/content # List content (published only)
// Documentation
GET /docs # Documentation home
// Static files
GET /images/* # Serve images
GET /media/serve/:key # Serve media files
// Health check
GET /health # System healthPublic Routes
Administrační trasy (routes)
Vyžaduje autentizaci + roli admina nebo editora.
// Dashboard
GET /admin/ # Admin dashboard
GET /admin/api/stats # Dashboard statistics (HTMX)
GET /admin/api/system-status # System status (HTMX)
// Collections
GET /admin/collections # List collections
GET /admin/collections/new # New collection form
POST /admin/collections # Create collection
GET /admin/collections/:id # Edit collection
PUT /admin/collections/:id # Update collection
DELETE /admin/collections/:id # Delete collection
// Content
GET /admin/content/ # List content
GET /admin/content/new # New content form
POST /admin/content/ # Create content
GET /admin/content/:id/edit # Edit content form
PUT /admin/content/:id # Update content
DELETE /admin/content/:id # Delete content
// Media
GET /admin/media/ # Media library
GET /admin/media/search # Search media (HTMX)
GET /admin/media/:id/details # File details (HTMX)
POST /admin/media/upload # Upload files
PUT /admin/media/:id # Update metadata
DELETE /admin/media/:id # Delete file
// Users
GET /admin/users # List users
POST /admin/users/:id/toggle # Toggle user status
GET /admin/users/export # Export users CSV
// Plugins
GET /admin/plugins # List plugins
POST /admin/plugins/:id/toggle # Toggle plugin status
GET /admin/plugins/:id # Plugin details
// Settings
GET /admin/settings # Settings page
GET /admin/settings/:tab # Settings tab
POST /admin/settings # Save settings
Admin Routes
Vytváření vlastního middlewaru
Základní struktura middlewaru
import { Context, Next } from 'hono'
type Bindings = {
DB: D1Database
KV: KVNamespace
}
type Variables = {
user?: {
userId: string
email: string
role: string
}
customData?: any
}
export function customMiddleware() {
return async (c: Context<{ Bindings: Bindings; Variables: Variables }>, next: Next) => {
// 1. Pre-processing (before route handler)
console.log('Before route handler')
// 2. Set context variables
c.set('customData', { foo: 'bar' })
// 3. Call next middleware/handler
await next()
// 4. Post-processing (after route handler)
console.log('After route handler')
}
}Middleware Template
Příklad: Middleware pro omezení počtu požadavků (Rate Limiting)
export function rateLimitMiddleware(maxRequests: number, windowMs: number) {
const requests = new Map<string, { count: number; resetTime: number }>()
return async (c: Context, next: Next) => {
const clientId = c.req.header('cf-connecting-ip') || 'unknown'
const now = Date.now()
// Get or create rate limit record
let record = requests.get(clientId)
if (!record || now > record.resetTime) {
record = { count: 0, resetTime: now + windowMs }
requests.set(clientId, record)
}
// Check rate limit
if (record.count >= maxRequests) {
const retryAfter = Math.ceil((record.resetTime - now) / 1000)
c.header('Retry-After', retryAfter.toString())
return c.json({ error: 'Rate limit exceeded' }, 429)
}
// Increment counter
record.count++
// Add rate limit headers
c.header('X-RateLimit-Limit', maxRequests.toString())
c.header('X-RateLimit-Remaining', (maxRequests - record.count).toString())
c.header('X-RateLimit-Reset', record.resetTime.toString())
await next()
}
}
// Usage
app.use('/api/\*', rateLimitMiddleware(100, 60000)) // 100 requests per minute
Rate Limiter
Middleware Best Practices
- Udržujte middleware zaměřený na jedinou zodpovědnost - Minimalizujte databázové dotazy v middlewaru - Používejte kontextové proměnné pro předávání dat mezi middlewarem
- Zpracovávejte chyby elegantně a poskytujte smysluplné chybové zprávy - Zvažte dopad pořadí spouštění middlewaru na výkon - Testujte middleware samostatně před integrací
Další kroky