SiamCafe · Blog
Lit Element Disaster Recovery Plan —
บทความ

Lit Element Disaster Recovery Plan —

เผยแพร่ 28 พฤษภาคม 2569

Lit Element DR

Lit Element Web Components Disaster Recovery CDN Failover Service Worker Offline State Recovery Error Boundary Rollback Health Check Resilience Progressive Enhancement

DR StrategyRTORPOComplexityเหมาะกับ
CDN Failover30 วินาที0ปานกลางStatic Assets
Service Worker0 (Cached)Last cacheสูงOffline Apps
State RecoveryทันทีLast saveต่ำForm Data
Rollback Deploy2-5 นาทีPrevious verต่ำBad Deploys

Lit Element Components

=== Lit Element with Error Boundary ===

npm install lit

error-boundary.ts

import { LitElement, html, css } from 'lit';

import { customElement, property, state } from 'lit/decorators.js';

@customElement('error-boundary')

export class ErrorBoundary extends LitElement {

@state() hasError = false;

@state() errorMessage = '';

static styles = css`

.error-container {

padding: 20px;

border: 2px solid #e74c3c;

border-radius: 8px;

background: #fdf2f2;

text-align: center;

}

.retry-btn {

margin-top: 12px;

padding: 8px 16px;

background: #3498db;

color: white;

border: none;

border-radius: 4px;

cursor: pointer;

}

`;

connectedCallback() {

super.connectedCallback();

window.addEventListener('error', this._handleError.bind(this));

window.addEventListener('unhandledrejection', this._handleRejection.bind(this));

}

_handleError(event) {

this.hasError = true;

this.errorMessage = event.message;

this._reportError(event);

}

_handleRejection(event) {

this.hasError = true;

this.errorMessage = event.reason?.message || 'Async error';

}

_reportError(error) {

fetch('/api/errors', {

method: 'POST',

body: JSON.stringify({ error: error.message, stack: error.stack })

}).catch(() => {});

}

_retry() {

this.hasError = false;

this.errorMessage = '';

}

render() {

if (this.hasError) {

return html`

<div class="error-container">

<h3>Something went wrong</h3>

# <button class="retry-btn" @click=>Retry</button>

</div>`;

}

return html`<slot></slot>`;

}

}

from dataclasses import dataclass

@dataclass

class Component:

name: str

type: str

size_kb: float

lazy: bool

cached: bool

status: str

components = [

Component("app-shell", "Layout", 8.5, False, True, "Active"),

Component("error-boundary", "Utility", 2.1, False, True, "Active"),

Component("offline-banner", "UI", 1.5, False, True, "Active"),

Component("data-table", "Feature", 15.2, True, True, "Active"),

Component("chart-widget", "Feature", 25.0, True, True, "Active"),

Component("form-wizard", "Feature", 12.0, True, True, "Active"),

Component("notification-toast", "UI", 3.0, False, True, "Active"),

]

print("=== Lit Components ===")

total_size = sum(c.size_kb for c in components)

for c in components:

lazy = "Lazy" if c.lazy else "Eager"

cached = "Cached" if c.cached else "No Cache"

print(f" [{c.status}] {c.name} ({c.type})")

print(f" Size: {c.size_kb}KB | Load: {lazy} | {cached}")

print(f"\n Total Bundle: {total_size:.1f}KB")

Service Worker

=== Service Worker for DR ===

sw.js — Cache-first with Network Fallback

const CACHE_NAME = 'app-v2.1.0';

const OFFLINE_URL = '/offline.html';

const PRECACHE_URLS = [

'/',

'/index.html',

'/offline.html',

'/assets/app-shell.js',

'/assets/error-boundary.js',

'/assets/styles.css',

'/assets/logo.svg',

];

self.addEventListener('install', (event) => {

event.waitUntil(

caches.open(CACHE_NAME).then((cache) => {

return cache.addAll(PRECACHE_URLS);

})

);

self.skipWaiting();

});

self.addEventListener('fetch', (event) => {

if (event.request.mode === 'navigate') {

event.respondWith(

fetch(event.request).catch(() => {

return caches.match(OFFLINE_URL);

})

);

return;

}

event.respondWith(

caches.match(event.request).then((cached) => {

if (cached) return cached;

return fetch(event.request).then((response) => {

const clone = response.clone();

caches.open(CACHE_NAME).then((cache) => {

cache.put(event.request, clone);

});

return response;

}).catch(() => {

return new Response('Offline', { status: 503 });

});

})

);

});

@dataclass

class CacheStrategy:

pattern: str

strategy: str

ttl: str

fallback: str

strategies = [

CacheStrategy("HTML Pages", "Network-first", "5 min", "Offline page"),

CacheStrategy("JS Bundles", "Cache-first", "30 days", "Cached version"),

CacheStrategy("CSS Styles", "Cache-first", "30 days", "Cached version"),

CacheStrategy("Images", "Cache-first", "7 days", "Placeholder"),

CacheStrategy("API Data", "Network-first", "1 min", "Stale cache"),

CacheStrategy("Fonts", "Cache-first", "90 days", "System font"),

]

print("\n=== Cache Strategies ===")

for s in strategies:

print(f" [{s.strategy}] {s.pattern}")

print(f" TTL: {s.ttl} | Fallback: {s.fallback}")

DR Plan

# === Disaster Recovery Plan ===

@dataclass
class DRScenario:
    scenario: str
    impact: str
    detection: str
    recovery: str
    rto: str

scenarios = [
    DRScenario("CDN Down", "Assets ไม่โหลด", "Health check fail", "Switch to backup CDN", "30s"),
    DRScenario("API Down", "Data ไม่โหลด", "API health check", "Show cached data + retry", "0s"),
    DRScenario("Bad Deploy", "App broken", "Error rate spike", "Rollback to previous version", "2min"),
    DRScenario("DNS Failure", "Site unreachable", "External monitor", "Switch DNS provider", "5min"),
    DRScenario("DB Corruption", "Data loss", "Data integrity check", "Restore from backup", "30min"),
    DRScenario("DDoS Attack", "Site slow/down", "Traffic spike alert", "Enable WAF + rate limit", "5min"),
]

print("DR Scenarios:")
for s in scenarios:
    print(f"  [{s.rto}] {s.scenario}")
    print(f"    Impact: {s.impact}")
    print(f"    Detection: {s.detection}")
    print(f"    Recovery: {s.recovery}")

# DR Checklist
checklist = [
    "Service Worker: Precache critical assets",
    "CDN Failover: Backup CDN configured and tested",
    "State Backup: Auto-save to LocalStorage every 30s",
    "Error Boundary: Wrap all route components",
    "Offline Page: Custom offline.html with retry",
    "Health Check: Monitor CDN API DNS every 30s",
    "Rollback: One-click rollback to previous deploy",
    "Runbook: Document every DR scenario and steps",
]

print(f"\n\nDR Checklist:")
for i, c in enumerate(checklist, 1):
    print(f"  {i}. {c}")

เคล็ดลับ

  • SW: Service Worker Precache Critical Assets ทุกครั้ง
  • Error: Error Boundary ครอบทุก Component สำคัญ
  • Cache: Cache-first สำหรับ Static, Network-first สำหรับ Data
  • Rollback: ต้อง Rollback ได้ภายใน 2 นาทีเสมอ
  • Test: ซ้อม DR ทุกเดือน ทดสอบทุก Scenario

Lit Element คืออะไร

Library Web Components เล็ก เร็ว TypeScript Reactive Properties Template Scoped Styles Reusable React Vue Angular Google YouTube