INTEGRITY Документация

Примеры API

В следующих примерах показано, как с помощью REST API и TypeScript SDK Cloudflare разворачивать Workers и управлять ими программно.

Предварительные требования

Перед использованием этих примеров вам потребуется:

Чтобы увидеть примеры использования SDK, установите Cloudflare SDK:

npm install cloudflare

Разверните пользовательский Worker

Загрузите скрипт Worker в свой dispatch namespace. Это основная операция, которую выполняет ваша платформа при развёртывании кода клиентами.

# First, create the worker script file
cat > worker.mjs << 'EOF'
export default {
  async fetch(request, env, ctx) {
    return new Response("Hello from user Worker!");
  },
};
EOF

# Deploy using multipart form (required for ES modules)
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts/$SCRIPT_NAME" \
  -H "Authorization: Bearer $API_TOKEN" \
  -F 'metadata={"main_module": "worker.mjs"};type=application/json' \
  -F '[email protected];type=application/javascript+module'
import Cloudflare from "cloudflare";

const client = new Cloudflare({
	apiToken: process.env.API_TOKEN,
});

async function deployUserWorker(
	accountId: string,
	namespace: string,
	scriptName: string,
	scriptContent: string,
) {
	const scriptFile = new File([scriptContent], `${scriptName}.mjs`, {
		type: "application/javascript+module",
	});

	const result =
		await client.workersForPlatforms.dispatch.namespaces.scripts.update(
			namespace,
			scriptName,
			{
				account_id: accountId,
				metadata: {
					main_module: `${scriptName}.mjs`,
				},
				files: [scriptFile],
			},
		);

	return result;
}

// Usage
await deployUserWorker(
	"your-account-id",
	"production",
	"customer-123",
	`export default {
  async fetch(request, env, ctx) {
    return new Response("Hello from customer 123!");
  },
};`,
);

Разверните с привязками и тегами

Используйте привязки чтобы дать каждому пользовательскому Worker собственные ресурсы, например хранилище KV или базу данных. Используйте теги чтобы группировать Workers по ID клиента, ID проекта или типу тарифного плана для массовых операций.

В следующем примере показано, как развернуть Worker с собственным пространством имён KV и прикреплёнными тегами:

curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts/$SCRIPT_NAME" \
  -H "Authorization: Bearer $API_TOKEN" \
  -F 'metadata={"main_module": "worker.mjs", "bindings": [{"type": "kv_namespace", "name": "MY_KV", "namespace_id": "your-kv-namespace-id"}], "tags": ["customer-123", "production", "pro-plan"], "compatibility_date": "2024-01-01"};type=application/json' \
  -F '[email protected];type=application/javascript+module'
import Cloudflare from "cloudflare";

const client = new Cloudflare({
	apiToken: process.env.API_TOKEN,
});

async function deployWorkerWithBindingsAndTags(
	accountId: string,
	namespace: string,
	scriptName: string,
	scriptContent: string,
	kvNamespaceId: string,
	tags: string[],
) {
	const scriptFile = new File([scriptContent], `${scriptName}.mjs`, {
		type: "application/javascript+module",
	});

	const result =
		await client.workersForPlatforms.dispatch.namespaces.scripts.update(
			namespace,
			scriptName,
			{
				account_id: accountId,
				metadata: {
					main_module: `${scriptName}.mjs`,
					compatibility_date: "2024-01-01",
					bindings: [
						{
							type: "kv_namespace",
							name: "MY_KV",
							namespace_id: kvNamespaceId,
						},
					],
					tags: tags, // e.g., ["customer-123", "production", "pro-plan"]
				},
				files: [scriptFile],
			},
		);

	return result;
}

// Usage
const scriptContent = `export default {
  async fetch(request, env, ctx) {
    const value = await env.MY_KV.get("key") || "default";
    return new Response(value);
  },
};`;

await deployWorkerWithBindingsAndTags(
	"your-account-id",
	"production",
	"customer-123-app",
	scriptContent,
	"kv-namespace-id",
	["customer-123", "production", "pro-plan"],
);

Подробнее см. в Bindings и Теги.

Разверните Worker со статическими ресурсами

Разверните Worker, который обслуживает статические файлы (HTML, CSS, JavaScript, изображения). Этот процесс состоит из трёх шагов:

  1. Создайте upload session с манифестом файлов
  2. Загрузите файлы ресурсов
  3. Разверните Worker с привязкой assets

Дополнительные сведения о настройке статических ресурсов и доступных параметрах см. в Static Assets.

Шаг 1: Создайте сеанс загрузки

curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts/$SCRIPT_NAME/assets-upload-session" \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "manifest": {
      "/index.html": {
        "hash": "<sha256-hash-first-16-bytes-hex>",
        "size": 1234
      },
      "/styles.css": {
        "hash": "<sha256-hash-first-16-bytes-hex>",
        "size": 567
      }
    }
  }'

Ответ включает jwt токен и buckets массив, указывающий, какие файлы необходимо загрузить.

Шаг 2: Загрузите ресурсы

curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/assets/upload?base64=true" \
  -H "Authorization: Bearer $JWT_FROM_STEP_1" \
  -F '<hash1>=<base64-encoded-content>' \
  -F '<hash2>=<base64-encoded-content>'

Шаг 3: Разверните Worker с ресурсами

curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts/$SCRIPT_NAME" \
  -H "Authorization: Bearer $API_TOKEN" \
  -F 'metadata={"main_module": "worker.mjs", "assets": {"jwt": "<completion-token>"}, "bindings": [{"type": "assets", "name": "ASSETS"}]};type=application/json' \
  -F 'worker.mjs=export default { async fetch(request, env) { return env.ASSETS.fetch(request); } };type=application/javascript+module'
interface AssetFile {
	path: string; // e.g., "/index.html"
	content: string; // base64 encoded content
	size: number; // file size in bytes
}

async function hashContent(base64Content: string): Promise<string> {
	const binaryString = atob(base64Content);
	const bytes = new Uint8Array(binaryString.length);
	for (let i = 0; i < binaryString.length; i++) {
		bytes[i] = binaryString.charCodeAt(i);
	}
	const hashBuffer = await crypto.subtle.digest("SHA-256", bytes);
	const hashArray = Array.from(new Uint8Array(hashBuffer));
	// Use first 16 bytes (32 hex chars) per API requirement
	return hashArray
		.slice(0, 16)
		.map((b) => b.toString(16).padStart(2, "0"))
		.join("");
}

async function deployWorkerWithAssets(
	accountId: string,
	namespace: string,
	scriptName: string,
	assets: AssetFile[],
) {
	const apiToken = process.env.API_TOKEN;
	const baseUrl = `https://api.cloudflare.com/client/v4/accounts/${accountId}/workers`;

	// Step 1: Build manifest
	const manifest: Record<string, { hash: string; size: number }> = {};
	const hashToAsset = new Map<string, AssetFile>();

	for (const asset of assets) {
		const hash = await hashContent(asset.content);
		const path = asset.path.startsWith("/") ? asset.path : "/" + asset.path;
		manifest[path] = { hash, size: asset.size };
		hashToAsset.set(hash, asset);
	}

	// Step 2: Create upload session
	const sessionResponse = await fetch(
		`${baseUrl}/dispatch/namespaces/${namespace}/scripts/${scriptName}/assets-upload-session`,
		{
			method: "POST",
			headers: {
				Authorization: `Bearer ${apiToken}`,
				"Content-Type": "application/json",
			},
			body: JSON.stringify({ manifest }),
		},
	);

	const sessionData = (await sessionResponse.json()) as {
		success: boolean;
		result?: { jwt: string; buckets?: string[][] };
	};

	if (!sessionData.success || !sessionData.result) {
		throw new Error("Failed to create upload session");
	}

	let completionToken = sessionData.result.jwt;
	const buckets = sessionData.result.buckets;

	// Step 3: Upload assets in buckets
	if (buckets && buckets.length > 0) {
		for (const bucket of buckets) {
			const formData = new FormData();
			for (const hash of bucket) {
				const asset = hashToAsset.get(hash);
				if (asset) {
					formData.append(hash, asset.content);
				}
			}

			const uploadResponse = await fetch(
				`${baseUrl}/assets/upload?base64=true`,
				{
					method: "POST",
					headers: { Authorization: `Bearer ${completionToken}` },
					body: formData,
				},
			);

			const uploadData = (await uploadResponse.json()) as {
				success: boolean;
				result?: { jwt?: string };
			};

			if (uploadData.result?.jwt) {
				completionToken = uploadData.result.jwt;
			}
		}
	}

	// Step 4: Deploy worker with assets binding
	const workerCode = `
export default {
  async fetch(request, env) {
    return env.ASSETS.fetch(request);
  }
};`;

	const deployFormData = new FormData();
	const metadata = {
		main_module: `${scriptName}.mjs`,
		assets: { jwt: completionToken },
		bindings: [{ type: "assets", name: "ASSETS" }],
	};

	deployFormData.append(
		"metadata",
		new Blob([JSON.stringify(metadata)], { type: "application/json" }),
	);
	deployFormData.append(
		`${scriptName}.mjs`,
		new Blob([workerCode], { type: "application/javascript+module" }),
	);

	const deployResponse = await fetch(
		`${baseUrl}/dispatch/namespaces/${namespace}/scripts/${scriptName}`,
		{
			method: "PUT",
			headers: { Authorization: `Bearer ${apiToken}` },
			body: deployFormData,
		},
	);

	return deployResponse.json();
}

// Usage
await deployWorkerWithAssets("your-account-id", "production", "customer-site", [
	{
		path: "/index.html",
		content: btoa("<html><body>Hello World</body></html>"),
		size: 37,
	},
	{
		path: "/styles.css",
		content: btoa("body { font-family: sans-serif; }"),
		size: 33,
	},
]);

Получить список Workers в пространстве имён

Получите все пользовательские Workers, развёрнутые в пространстве имён.

curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts" \
  -H "Authorization: Bearer $API_TOKEN"
async function listWorkers(accountId: string, namespace: string) {
	const response = await fetch(
		`https://api.cloudflare.com/client/v4/accounts/${accountId}/workers/dispatch/namespaces/${namespace}/scripts`,
		{
			headers: {
				Authorization: `Bearer ${process.env.API_TOKEN}`,
			},
		},
	);

	const data = (await response.json()) as {
		success: boolean;
		result: Array<{ id: string; tags?: string[] }>;
	};

	return data.result;
}

// Usage
const workers = await listWorkers("your-account-id", "production");
console.log(workers);

Удаление Workers по тегу

Удалите все Workers, соответствующие фильтру тегов. Это полезно, если клиент удаляет свой аккаунт и вам нужно сразу убрать все его Workers.

Удалить все Workers с тегом customer-123:

curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts?tags=customer-123:yes" \
  -H "Authorization: Bearer $API_TOKEN"
async function deleteWorkersByTag(
	accountId: string,
	namespace: string,
	tag: string,
) {
	const response = await fetch(
		`https://api.cloudflare.com/client/v4/accounts/${accountId}/workers/dispatch/namespaces/${namespace}/scripts?tags=${tag}:yes`,
		{
			method: "DELETE",
			headers: {
				Authorization: `Bearer ${process.env.API_TOKEN}`,
			},
		},
	);

	return response.json();
}

// Usage: Delete all Workers for a customer
await deleteWorkersByTag("your-account-id", "production", "customer-123");

Удаление одного Worker

Удалите определённый Worker по имени.

curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts/$SCRIPT_NAME" \
  -H "Authorization: Bearer $API_TOKEN"
import Cloudflare from "cloudflare";

const client = new Cloudflare({
	apiToken: process.env.API_TOKEN,
});

async function deleteWorker(
	accountId: string,
	namespace: string,
	scriptName: string,
) {
	const result =
		await client.workersForPlatforms.dispatch.namespaces.scripts.delete(
			namespace,
			scriptName,
			{ account_id: accountId },
		);

	return result;
}

// Usage
await deleteWorker("your-account-id", "production", "customer-123");