mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 06:11:27 -07:00
update skills
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
### Common Patterns
|
||||
|
||||
**1. Forward request to assets:**
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
return env.ASSETS.fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**2. Fetch specific asset by path:**
|
||||
|
||||
```typescript
|
||||
const response = await env.ASSETS.fetch("https://assets.local/logo.png");
|
||||
```
|
||||
|
||||
**3. Modify request before fetching asset:**
|
||||
|
||||
```typescript
|
||||
const url = new URL(request.url);
|
||||
url.pathname = "/index.html";
|
||||
return env.ASSETS.fetch(new Request(url, request));
|
||||
```
|
||||
|
||||
**4. Transform asset response:**
|
||||
|
||||
```typescript
|
||||
const response = await env.ASSETS.fetch(request);
|
||||
const modifiedResponse = new Response(response.body, response);
|
||||
modifiedResponse.headers.set("X-Custom-Header", "value");
|
||||
modifiedResponse.headers.set("Cache-Control", "public, max-age=3600");
|
||||
return modifiedResponse;
|
||||
```
|
||||
|
||||
**5. Conditional asset serving:**
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname === '/') {
|
||||
return env.ASSETS.fetch('/index.html');
|
||||
}
|
||||
return env.ASSETS.fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**6. SPA with API routes:**
|
||||
|
||||
Most common full-stack pattern - static SPA with backend API:
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname.startsWith('/api/')) {
|
||||
return handleAPI(request, env);
|
||||
}
|
||||
return env.ASSETS.fetch(request);
|
||||
}
|
||||
};
|
||||
|
||||
async function handleAPI(request: Request, env: Env): Promise<Response> {
|
||||
return new Response(JSON.stringify({ status: 'ok' }), {
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Config:** Set `run_worker_first: ["/api/*"]` (see configuration.md:66-106)
|
||||
|
||||
**7. Auth gating for protected assets:**
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname.startsWith('/admin/')) {
|
||||
const session = await validateSession(request, env);
|
||||
if (!session) {
|
||||
return Response.redirect('/login', 302);
|
||||
}
|
||||
}
|
||||
return env.ASSETS.fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Config:** Set `run_worker_first: ["/admin/*"]`
|
||||
|
||||
**8. Custom headers for security:**
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const response = await env.ASSETS.fetch(request);
|
||||
const secureResponse = new Response(response.body, response);
|
||||
secureResponse.headers.set('X-Frame-Options', 'DENY');
|
||||
secureResponse.headers.set('X-Content-Type-Options', 'nosniff');
|
||||
secureResponse.headers.set('Content-Security-Policy', "default-src 'self'");
|
||||
return secureResponse;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**9. A/B testing via cookies:**
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const cookies = request.headers.get('Cookie') || '';
|
||||
const variant = cookies.includes('variant=b') ? 'b' : 'a';
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname === '/') {
|
||||
return env.ASSETS.fetch(`/index-${variant}.html`);
|
||||
}
|
||||
return env.ASSETS.fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**10. Locale-based routing:**
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const locale = request.headers.get('Accept-Language')?.split(',')[0] || 'en';
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname === '/') {
|
||||
return env.ASSETS.fetch(`/${locale}/index.html`);
|
||||
}
|
||||
if (!url.pathname.startsWith(`/${locale}/`)) {
|
||||
url.pathname = `/${locale}${url.pathname}`;
|
||||
}
|
||||
return env.ASSETS.fetch(url);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**11. OAuth callback handling:**
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname === '/auth/callback') {
|
||||
const code = url.searchParams.get('code');
|
||||
if (code) {
|
||||
const session = await exchangeCode(code, env);
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: {
|
||||
'Location': '/',
|
||||
'Set-Cookie': `session=${session}; HttpOnly; Secure; SameSite=Lax`
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return env.ASSETS.fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Config:** Set `run_worker_first: ["/auth/*"]`
|
||||
|
||||
**12. Cache control override:**
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const response = await env.ASSETS.fetch(request);
|
||||
const url = new URL(request.url);
|
||||
// Immutable assets (hashed filenames)
|
||||
if (/\.[a-f0-9]{8,}\.(js|css|png|jpg)$/.test(url.pathname)) {
|
||||
return new Response(response.body, {
|
||||
...response,
|
||||
headers: {
|
||||
...Object.fromEntries(response.headers),
|
||||
'Cache-Control': 'public, max-age=31536000, immutable'
|
||||
}
|
||||
});
|
||||
}
|
||||
return response;
|
||||
}
|
||||
};
|
||||
```
|
||||
Reference in New Issue
Block a user