Initial commit
This commit is contained in:
312
templates/variants-management.ts
Normal file
312
templates/variants-management.ts
Normal file
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* Cloudflare Images - Variants Management
|
||||
*
|
||||
* Create, list, update, and delete image variants.
|
||||
* Variants define predefined transformations for different use cases.
|
||||
*/
|
||||
|
||||
interface Env {
|
||||
IMAGES_ACCOUNT_ID: string;
|
||||
IMAGES_API_TOKEN: string;
|
||||
}
|
||||
|
||||
interface VariantOptions {
|
||||
fit?: 'scale-down' | 'contain' | 'cover' | 'crop' | 'pad';
|
||||
width?: number;
|
||||
height?: number;
|
||||
metadata?: 'none' | 'copyright' | 'keep';
|
||||
}
|
||||
|
||||
interface Variant {
|
||||
id: string;
|
||||
options: VariantOptions;
|
||||
neverRequireSignedURLs?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new variant
|
||||
*/
|
||||
export async function createVariant(
|
||||
id: string,
|
||||
options: VariantOptions,
|
||||
neverRequireSignedURLs: boolean = false,
|
||||
env: Env
|
||||
): Promise<{ success: boolean; result?: Variant }> {
|
||||
const response = await fetch(
|
||||
`https://api.cloudflare.com/client/v4/accounts/${env.IMAGES_ACCOUNT_ID}/images/v1/variants`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${env.IMAGES_API_TOKEN}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id,
|
||||
options,
|
||||
neverRequireSignedURLs
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* List all variants
|
||||
*/
|
||||
export async function listVariants(
|
||||
env: Env
|
||||
): Promise<{ success: boolean; result?: { variants: Variant[] } }> {
|
||||
const response = await fetch(
|
||||
`https://api.cloudflare.com/client/v4/accounts/${env.IMAGES_ACCOUNT_ID}/images/v1/variants`,
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${env.IMAGES_API_TOKEN}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific variant
|
||||
*/
|
||||
export async function getVariant(
|
||||
id: string,
|
||||
env: Env
|
||||
): Promise<{ success: boolean; result?: Variant }> {
|
||||
const response = await fetch(
|
||||
`https://api.cloudflare.com/client/v4/accounts/${env.IMAGES_ACCOUNT_ID}/images/v1/variants/${id}`,
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${env.IMAGES_API_TOKEN}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a variant
|
||||
*/
|
||||
export async function updateVariant(
|
||||
id: string,
|
||||
options: VariantOptions,
|
||||
neverRequireSignedURLs?: boolean,
|
||||
env: Env
|
||||
): Promise<{ success: boolean; result?: Variant }> {
|
||||
const body: Record<string, unknown> = { options };
|
||||
if (neverRequireSignedURLs !== undefined) {
|
||||
body.neverRequireSignedURLs = neverRequireSignedURLs;
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.cloudflare.com/client/v4/accounts/${env.IMAGES_ACCOUNT_ID}/images/v1/variants/${id}`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${env.IMAGES_API_TOKEN}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
}
|
||||
);
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a variant
|
||||
*/
|
||||
export async function deleteVariant(
|
||||
id: string,
|
||||
env: Env
|
||||
): Promise<{ success: boolean }> {
|
||||
const response = await fetch(
|
||||
`https://api.cloudflare.com/client/v4/accounts/${env.IMAGES_ACCOUNT_ID}/images/v1/variants/${id}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${env.IMAGES_API_TOKEN}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable flexible variants (dynamic transformations)
|
||||
*/
|
||||
export async function enableFlexibleVariants(
|
||||
enabled: boolean,
|
||||
env: Env
|
||||
): Promise<{ success: boolean }> {
|
||||
const response = await fetch(
|
||||
`https://api.cloudflare.com/client/v4/accounts/${env.IMAGES_ACCOUNT_ID}/images/v1/config`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${env.IMAGES_API_TOKEN}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
flexible_variants: enabled
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Example Worker endpoint
|
||||
*/
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Create variant: POST /api/variants
|
||||
if (request.method === 'POST' && url.pathname === '/api/variants') {
|
||||
try {
|
||||
const body = await request.json<{
|
||||
id: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
fit?: string;
|
||||
}>();
|
||||
|
||||
const result = await createVariant(
|
||||
body.id,
|
||||
{
|
||||
width: body.width,
|
||||
height: body.height,
|
||||
fit: body.fit as VariantOptions['fit'],
|
||||
metadata: 'none'
|
||||
},
|
||||
false,
|
||||
env
|
||||
);
|
||||
|
||||
return Response.json(result);
|
||||
|
||||
} catch (error) {
|
||||
return Response.json(
|
||||
{ error: error instanceof Error ? error.message : 'Failed to create variant' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// List variants: GET /api/variants
|
||||
if (request.method === 'GET' && url.pathname === '/api/variants') {
|
||||
const result = await listVariants(env);
|
||||
return Response.json(result);
|
||||
}
|
||||
|
||||
return Response.json({ error: 'Not found' }, { status: 404 });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Common variant presets
|
||||
*/
|
||||
export async function setupCommonVariants(env: Env): Promise<void> {
|
||||
// Thumbnail
|
||||
await createVariant('thumbnail', {
|
||||
width: 300,
|
||||
height: 300,
|
||||
fit: 'cover',
|
||||
metadata: 'none'
|
||||
}, false, env);
|
||||
|
||||
// Avatar
|
||||
await createVariant('avatar', {
|
||||
width: 200,
|
||||
height: 200,
|
||||
fit: 'cover',
|
||||
metadata: 'none'
|
||||
}, false, env);
|
||||
|
||||
// Small
|
||||
await createVariant('small', {
|
||||
width: 480,
|
||||
fit: 'scale-down',
|
||||
metadata: 'none'
|
||||
}, false, env);
|
||||
|
||||
// Medium
|
||||
await createVariant('medium', {
|
||||
width: 768,
|
||||
fit: 'scale-down',
|
||||
metadata: 'none'
|
||||
}, false, env);
|
||||
|
||||
// Large
|
||||
await createVariant('large', {
|
||||
width: 1920,
|
||||
fit: 'scale-down',
|
||||
metadata: 'none'
|
||||
}, false, env);
|
||||
|
||||
// Hero (wide)
|
||||
await createVariant('hero', {
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
fit: 'cover',
|
||||
metadata: 'none'
|
||||
}, false, env);
|
||||
|
||||
// Product (square)
|
||||
await createVariant('product', {
|
||||
width: 800,
|
||||
height: 800,
|
||||
fit: 'contain',
|
||||
metadata: 'none'
|
||||
}, false, env);
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage examples:
|
||||
*
|
||||
* ```typescript
|
||||
* // Create a variant
|
||||
* await createVariant('thumbnail', {
|
||||
* width: 300,
|
||||
* height: 300,
|
||||
* fit: 'cover',
|
||||
* metadata: 'none'
|
||||
* }, false, env);
|
||||
*
|
||||
* // List all variants
|
||||
* const { result } = await listVariants(env);
|
||||
* console.log(result?.variants);
|
||||
*
|
||||
* // Update a variant
|
||||
* await updateVariant('thumbnail', {
|
||||
* width: 350, // Changed from 300
|
||||
* height: 350,
|
||||
* fit: 'cover'
|
||||
* }, undefined, env);
|
||||
*
|
||||
* // Delete a variant
|
||||
* await deleteVariant('old-variant', env);
|
||||
*
|
||||
* // Enable flexible variants (dynamic transformations)
|
||||
* await enableFlexibleVariants(true, env);
|
||||
* // Now can use: /w=400,sharpen=3 in URLs
|
||||
*
|
||||
* // Use variant in image URL
|
||||
* const imageURL = `https://imagedelivery.net/${accountHash}/${imageId}/thumbnail`;
|
||||
* ```
|
||||
*
|
||||
* LIMITS:
|
||||
* - Maximum 100 named variants per account
|
||||
* - Flexible variants: unlimited dynamic transformations (but can't use with signed URLs)
|
||||
*
|
||||
* WHEN TO USE:
|
||||
* - Named variants: Consistent sizes, private images (signed URLs), predictable URLs
|
||||
* - Flexible variants: Dynamic sizing, public images only, rapid prototyping
|
||||
*/
|
||||
Reference in New Issue
Block a user