← Cloudflare Pages / pages / functions
Pages Plugins
Cloudflare поддерживает набор официальных Pages Plugins для использования в ваших проектах Pages:
- Cloudflare Access
- Google Chat
- GraphQL
- hCaptcha
- Honeycomb
- Sentry
- Статические формы
- Stytch
- Turnstile
- Community Plugins
- vercel/og
Разработка Pages Plugin
Pages Plugin представляет собой распространяемый пакет Pages Functions со встроенной маршрутизацией и функциональностью. Разработчики могут подключить Plugin в любом месте своего проекта Pages и передать ему параметры конфигурации. Plugins получают доступ ко всем возможностям Functions, включая middleware, параметризованные маршруты и статические ресурсы.
Например, Pages Plugin может:
- Перехватывайте HTML-страницы и внедряйте в них сторонний скрипт.
- Проксируйте API стороннего сервиса.
- Проверяйте заголовки авторизации.
- Обеспечивает полноценный веб-интерфейс администратора.
- Сохраняйте данные в KV или Durable Objects.
- Выполняйте серверный рендеринг (SSR) веб-страниц с данными из CMS.
- Отчёты об ошибках и мониторинг производительности.
По сути, Pages Plugin представляет собой библиотеку, с помощью которой разработчики могут глубоко интегрировать Functions в существующий проект Pages.
Использование плагина Pages
Разработчики могут расширять свои проекты, подключая Pages Plugin к маршруту своего приложения. Плагины Pages содержат инструкции о том, куда их обычно следует подключать (например, интерфейс администратора может быть подключен по адресу functions/admin/[[path]].ts, а логгер ошибок может быть подключён по адресу functions/_middleware.ts). Кроме того, каждый плагин может принимать определённую конфигурацию (например, с помощью API токена).
Пример статической формы
В этом примере вы создадите Pages Plugin, а затем подключите его к проекту.
Первый плагин должен:
- перехватывать HTML-формы.
- сохранять отправленные данные формы в KV.
- отвечать на отправленные формы собственным ответом разработчика.
1. Создайте новый Pages Plugin
Создайте package.json на следующее:
{
"name": "@cloudflare/static-form-interceptor",
"main": "dist/index.js",
"types": "index.d.ts",
"files": ["dist", "index.d.ts", "tsconfig.json"],
"scripts": {
"build": "npx wrangler pages functions build --plugin --outdir=dist",
"prepare": "npm run build"
}
}В нашем примере dist/index.js будет точкой входа для вашего Plugin. Это сгенерированный файл, собранный Wrangler с npm run build команду. Добавьте dist/ директорию в .gitignore.
Далее создайте functions директорию и начните писать код вашего Plugin. functions папка будет подключена разработчиком к определённому маршруту, поэтому продумайте структуру файлов заранее. Как правило:
- если вы хотите, чтобы Plugin работал только на одном выбранном разработчиком маршруте (например,
/foo), создайтеfunctions/index.tsфайл. - если вы хотите, чтобы Plugin монтировался и обрабатывал все запросы за пределами определённого пути (например,
/admin/loginи/admin/dashboard), создайтеfunctions/[[path]].tsфайл. - если вы хотите, чтобы Plugin перехватывал запросы, но при необходимости передавал их другим Functions или статическим ресурсам проекта, создайте
functions/_middleware.tsфайл.
Вы можете использовать сколько угодно файлов. Структура Plugin точно такая же, как у Functions в проекте Pages, за исключением того, что обработчики получают новое свойство объекта параметров, pluginArgs. Это свойство представляет собой параметр инициализации, который разработчик передает при подключении Plugin. С его помощью можно получать API-токены, пространства имен KV/Durable Object или любые другие данные, необходимые вашему Plugin для работы.
Возвращаясь к примеру со статической формой, если вы хотите перехватывать запросы и переопределять поведение HTML-формы, вам нужно создать functions/_middleware.ts. После этого разработчики смогут подключить ваш Plugin к одному маршруту или ко всему проекту.
class FormHandler {
element(element) {
const name = element.getAttribute("data-static-form-name");
element.setAttribute("method", "POST");
element.removeAttribute("action");
element.append(
`<input type="hidden" name="static-form-name" value="${name}" />`,
{ html: true },
);
}
}
export const onRequestGet = async (context) => {
// We first get the original response from the project
const response = await context.next();
// Then, using HTMLRewriter, we transform `form` elements with a `data-static-form-name` attribute, to tell them to POST to the current page
return new HTMLRewriter()
.on("form[data-static-form-name]", new FormHandler())
.transform(response);
};
export const onRequestPost = async (context) => {
// Parse the form
const formData = await context.request.formData();
const name = formData.get("static-form-name");
const entries = Object.fromEntries(
[...formData.entries()].filter(([name]) => name !== "static-form-name"),
);
// Get the arguments given to the Plugin by the developer
const { kv, respondWith } = context.pluginArgs;
// Store form data in KV under key `form-name:YYYY-MM-DDTHH:MM:SSZ`
const key = `${name}:${new Date().toISOString()}`;
context.waitUntil(kv.put(name, JSON.stringify(entries)));
// Respond with whatever the developer wants
const response = await respondWith({ formData });
return response;
};2. Типизируйте Pages Plugin
Чтобы упростить работу разработчиков, добавьте в Plugin типизацию TypeScript. Это позволит использовать функции автодополнения в IDE, а также гарантирует, что будут переданы все ожидаемые параметры.
В index.d.ts, экспортируйте функцию, которая принимает ваш pluginArgs и возвращает PagesFunction. Для примера со статической формой возьмите два свойства, kv, пространство имён KV, и respondWith, функция, которая принимает объект с formData свойство (FormData) и возвращает Promise для Response:
export type PluginArgs = {
kv: KVNamespace;
respondWith: (args: { formData: FormData }) => Promise<Response>;
};
export default function (args: PluginArgs): PagesFunction;3. Протестируйте Pages Plugin
Мы всё ещё работаем над созданием удобной среды тестирования для авторов Pages Plugins. Пожалуйста, потерпите, пока все эти элементы не будут готовы. А пока вы можете создать тестовый проект и вручную подключить к нему свой плагин для тестирования.
4. Опубликуйте Pages Plugin
Plugin можно распространять любым удобным способом. Среди популярных вариантов: публикация на npm ↗, показав его в каналах #what-i-built или #pages-discussions в нашем Discord для разработчиков ↗, и опубликовали исходный код на GitHub ↗.
Убедитесь, что вы подключили сгенерированный dist/ директории ваши типы (typings) index.d.ts, а также README.md с инструкциями о том, как разработчики могут использовать ваш Plugin.
5. Установите Pages Plugin
Если вы хотите включить Pages Plugin в приложение, сначала установите этот Plugin в проект.
Если вы еще не используете npm в вашем проекте выполните npm init чтобы создать package.json файл. У Plugin README.md обычно включает команду установки (например, npm install --save @cloudflare/static-form-interceptor).
6. Подключите свой Pages Plugin
README.md плагина обычно содержит инструкции по его подключению к вашему приложению. Вам нужно будет:
- Создайте
functionsдиректорию, если у вас ее еще нет. - Решите, где должен запускаться этот Plugin, и создайте соответствующий файл в
functionsкаталог. - Импортируйте Plugin и экспортируйте
onRequestметод в этом файле, инициализировав плагин с нужными ему аргументами.
В примере со статической формой созданный вами Plugin уже был реализован как middleware. Это значит, что он может работать как на одном маршруте, так и во всем проекте. Если бы на вашем сайте была одна контактная форма по адресу /contact, вы могли бы создать functions/contact.ts файл, чтобы перехватывать только этот маршрут. Также можно создать functions/_middleware.ts файл, чтобы перехватывать все остальные маршруты и любые формы, которые вы создадите в будущем. Вы как разработчик сами решаете, где может выполняться этот Plugin.
Plugin экспортирует по умолчанию функцию, которая принимает тот же параметр контекста, что и обычный обработчик Pages Functions.
import staticFormInterceptorPlugin from "@cloudflare/static-form-interceptor";
export const onRequest = (context) => {
return staticFormInterceptorPlugin({
kv: context.env.FORM_KV,
respondWith: async ({ formData }) => {
// Could call email/notification service here
const name = formData.get("name");
return new Response(`Thank you for your submission, ${name}!`);
},
})(context);
};7. Протестируйте свой Pages Plugin
Вы можете использовать wrangler pages dev чтобы протестировать проект Pages, включая все установленные Plugins. Не забудьте указать привязки KV и переменные окружения, которые ожидает Plugin.
После подключения Plugin к /contact маршрут, соответствующий HTML-файл может выглядеть так:
<!DOCTYPE html>
<html>
<body>
<h1>Contact us</h1>
<!-- Include the `data-static-form-name` attribute to name the submission -->
<form data-static-form-name="contact">
<label>
<span>Name</span>
<input type="text" autocomplete="name" name="name" />
</label>
<label>
<span>Message</span>
<textarea name="message"></textarea>
</label>
</form>
</body>
</html>Плагин должен подхватить data-static-form-name="contact" атрибут, задайте method="POST", вставьте в <input type="hidden" name="static-form-name" value="contact" /> элемент и перехватить POST отправки.
8. Разверните свой проект Pages
Убедитесь, что новый плагин добавлен в ваш package.json и что всё работает локально так, как вы и ожидаете. После этого можно git commit и git push чтобы запустить развертывание Cloudflare Pages.
Если у вас возникла проблема с каким-либо отдельным Plugin, создайте issue в баг-трекере этого Plugin.
Если у вас возникают какие-либо проблемы с Plugins в целом, поделитесь, пожалуйста, отзывом в канале #pages-discussions в Discord ↗! Нам не терпится увидеть, что вы создадите с помощью Plugins, и мы будем рады любым отзывам об опыте разработки. Напишите нам в канале Discord, если вам нужно что-то, что сделает Plugins ещё мощнее.
Свяжите свой Plugin в цепочку
Наконец, как и в случае с Pages Functions в целом, Plugins можно объединять в цепочку, чтобы совместить разные возможности. Middleware, определённый выше по структуре файловой системы, выполняется раньше остальных обработчиков, а отдельные файлы могут объединять Functions в цепочку с помощью массива, например так:
import sentryPlugin from "@cloudflare/pages-plugin-sentry";
import cloudflareAccessPlugin from "@cloudflare/pages-plugin-cloudflare-access";
import adminDashboardPlugin from "@cloudflare/a-fictional-admin-plugin";
export const onRequest = [
// Initialize a Sentry Plugin to capture any errors
sentryPlugin({ dsn: "https://sentry.io/welcome/xyz" }),
// Initialize a Cloudflare Access Plugin to ensure only administrators can access this protected route
cloudflareAccessPlugin({
domain: "https://test.cloudflareaccess.com",
aud: "4714c1358e65fe4b408ad6d432a5f878f08194bdb4752441fd56faefa9b2b6f2",
}),
// Populate the Sentry plugin with additional information about the current user
(context) => {
const email =
context.data.cloudflareAccessJWT.payload?.email || "service user";
context.data.sentry.setUser({ email });
return next();
},
// Finally, serve the admin dashboard plugin, knowing that errors will be captured and that every incoming request has been authenticated
adminDashboardPlugin(),
];