\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Новости\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/novosti/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/astro-2-5/\"],\"title\":[0,\"Astro 2.5 - Gorlov.\"],\"breadcrumbTitle\":[0,\"Astro 2.5\"],\"description\":[0,\"Мы только что выпустили Astro 2.5 с большим списком функций, включая:\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Мы только что выпустили Astro 2.5 с большим списком функций, включая:\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Astro 2.5 - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T15:21:48+00:00\"],\"url\":[0,\"http://igorlov.loc/astro-2-5/\"]}]}],\"title\":[0,\"Astro 2.5\"],\"uri\":[0,\"/astro-2-5/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"astro-2-5\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Новости\\\"],\\\"uri\\\":[0,\\\"/category/novosti/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Astro\\\"],\\\"id\\\":[0,\\\"dGVybTo2Mw==\\\"],\\\"uri\\\":[0,\\\"/tag/astro/\\\"]}],[0,{\\\"name\\\":[0,\\\"Новости\\\"],\\\"id\\\":[0,\\\"dGVybTox\\\"],\\\"uri\\\":[0,\\\"/category/novosti/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjEyMTQ5\"],\"node\":[0,{\"date\":[0,\"2023-04-30T16:46:52\"],\"content\":[0,\"\\n
Докеризация приложения Node.js может значительно упростить процесс развертывания и улучшить масштабируемость вашего приложения. Docker – это платформа контейнеризации, которая позволяет упаковать ваше приложение и его зависимости в контейнер, который затем может быть запущен на любой платформе, поддерживающей Docker.
\\n\\n\\n\\nВ этой статье мы рассмотрим шаги, необходимые для Dockerize приложения Node.js с нуля.
\\n\\n\\n\\n
Первым шагом будет создание приложения Node.js, которое мы хотим докеризировать. Для этого мы будем использовать фреймворк Express.js, который является популярным фреймворком веб-приложений для Node.js.
Чтобы создать приложение Express.js, нам сначала нужно установить Node.js и менеджер пакетов npm на нашу машину. После установки Node.js и npm мы можем создать новое приложение Express.js, выполнив следующую команду:
\\n\\n\\n\\nnpx express-generator myapp\\n
\\n\\n\\n\\nЭто создаст новое приложение Express.js в каталоге под названием myapp. После создания приложения мы можем перейти в каталог и установить его зависимости с помощью npm:
\\n\\n\\n\\ncd myapp\\nnpm install\\n
\\n\\n\\n\\nСледующим шагом будет создание Dockerfile, который представляет собой конфигурационный файл, описывающий, как создать образ Docker для нашего приложения Node.js.
\\n\\n\\n\\nСоздайте новый файл под названием Dockerfile в корневом каталоге вашего приложения и добавьте в него следующее содержимое:
\\n\\n\\n\\n# Use an official Node.js runtime as a parent image\\nFROM node:14-alpine\\n\\n# Set the working directory to /app\\nWORKDIR /app\\n\\n# Copy package.json and package-lock.json to the working directory\\nCOPY package*.json ./\\n\\n# Install dependencies\\nRUN npm install\\n\\n# Copy the rest of the application code to the working directory\\nCOPY . .\\n\\n# Expose port 3000\\nEXPOSE 3000\\n\\n# Start the application\\nCMD [ \\\"npm\\\", \\\"start\\\" ]\\n
\\n\\n\\n\\nДавайте пройдемся по этому Dockerfile строка за строкой:
\\n\\n\\n\\nFROM node:14-alpine: Эта строка указывает, что мы хотим использовать официальную среду исполнения Node.js 14 в качестве базового образа для нашего образа Docker. Alpine – это легкий дистрибутив Linux, который обычно используется для Docker-образов из-за своего небольшого размера.
\\n\\n\\n\\n
Теперь, когда у нас есть Dockerfile, мы можем использовать его для создания образа Docker для нашего приложения Node.js. Для этого мы воспользуемся командой docker build.
Откройте окно терминала, перейдите в корневой каталог вашего приложения и выполните следующую команду:
\\n\\n\\n\\ndocker build -t myapp .
\\n\\n\\n\\nЭта команда указывает Docker на сборку нового образа с использованием Dockerfile в текущем каталоге и присваивает ему имя myapp. Символ . в конце команды указывает контекст сборки, то есть каталог, который Docker использует в качестве корня процесса сборки.
\\n\\n\\n\\n
Теперь, когда у нас есть образ Docker для нашего приложения Node.js, мы можем использовать его для запуска контейнера Docker. Для этого мы воспользуемся командой docker run.
Откройте окно терминала и выполните следующую команду:
\\n\\n\\n\\ndocker run -p 3000:3000 myapp
\\n\\n\\n\\nЭта команда указывает Docker запустить новый контейнер, используя образ myapp, который мы только что создали, и сопоставить порт 3000 из контейнера с портом 3000 на хост-машине. Это позволит нам получить доступ к нашему приложению Node.js, запущенному внутри контейнера, через веб-браузер.
\\n\\n\\n\\nЕсли все настроено правильно, вы должны увидеть вывод в окне терминала, указывающий на то, что ваше приложение Node.js запущено. Теперь вы можете открыть веб-браузер и перейти по адресу http://localhost:3000, чтобы увидеть свое приложение в действии.
\\n\\n\\n\\nВ этой статье мы рассмотрели шаги, необходимые для Dockerize приложения Node.js с нуля, без копирования других. Мы начали с создания нового приложения Node.js с использованием фреймворка Express.js, затем создали Dockerfile, в котором описали, как создать образ Docker для нашего приложения. Затем мы создали образ Docker и запустили контейнер Docker, используя этот образ. Выполнив эти шаги, вы теперь должны иметь полностью Dockerized Node.js приложение, которое можно легко развернуть и масштабировать.
\\n\\n\\n\\nСпасибо за ваше время!
\\n\\n\\n\\r\\nДокеризация приложения Node.js может значительно упростить процесс развертывания и улучшить масштабируемость вашего приложения. Docker – это платформа контейнеризации, которая позволяет упаковать ваше приложение и его зависимости в контейнер, который затем может быть запущен на любой платформе, поддерживающей Docker. В этой статье мы рассмотрим шаги, необходимые для Dockerize приложения Node.js с нуля. Шаг 1: Создание приложения […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Как закодить\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/kak-zakodit/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/dokerizacziya-prilozheniya-node-js-s-nulya/\"],\"title\":[0,\"Докеризация приложения Node.js с нуля - Gorlov.\"],\"breadcrumbTitle\":[0,\"Докеризация приложения Node.js с нуля\"],\"description\":[0,\"Докеризация приложения Node.js может значительно упростить процесс развертывания и улучшить масштабируемость вашего приложения. Docker - это платформа\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Докеризация приложения Node.js может значительно упростить процесс развертывания и улучшить масштабируемость вашего приложения. Docker - это платформа\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Докеризация приложения Node.js с нуля - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:41:40+00:00\"],\"url\":[0,\"http://igorlov.loc/dokerizacziya-prilozheniya-node-js-s-nulya/\"]}]}],\"title\":[0,\"Докеризация приложения Node.js с нуля\"],\"uri\":[0,\"/dokerizacziya-prilozheniya-node-js-s-nulya/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"dokerizacziya-prilozheniya-node-js-s-nulya\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Docker\\\"],\\\"id\\\":[0,\\\"dGVybToxNjc=\\\"],\\\"uri\\\":[0,\\\"/tag/docker/\\\"]}],[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"id\\\":[0,\\\"dGVybToyNg==\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjEyMTQy\"],\"node\":[0,{\"date\":[0,\"2023-04-30T11:48:13\"],\"content\":[0,\"\\nWSL расшифровывается как Windows Subsystem for Linux. Это функция в Windows 10 и windows 11, которая позволяет разработчикам запускать дистрибутивы Linux на своих машинах Windows без необходимости использования виртуальной машины или двойной загрузки. Это означает, что разработчики могут использовать инструменты и приложения Linux на машине Windows без необходимости переключаться между операционными системами или создавать сложные среды виртуализации.
\\n\\n\\n\\nВ настоящее время доступны две версии WSL: WSL 1 и WSL 2. WSL 1 основана на уровне трансляции, который преобразует системные вызовы Linux в системные вызовы Windows, в то время как WSL 2 запускает полное ядро Linux в легковесной виртуальной машине. WSL 2 обеспечивает лучшую производительность и совместимость с приложениями Linux.
\\n\\n\\n\\nЕсли вы заинтересованы в установке и использовании WSL2, есть несколько важных команд, которые вы должны знать. В этой статье мы рассмотрим команды для установки WSL2 в вашей системе windows.
\\n\\n\\n\\nПрежде чем приступить к работе, важно отметить, что все эти команды необходимо запускать из открытой командной строки или терминала PowerShell. Для этого просто щелкните правой кнопкой мыши на значке Command Prompt или PowerShell и выберите “Запуск от имени администратора”.
\\n\\n\\n\\nЧтобы установить WSL, выполните следующую команду:
\\n\\n\\n\\nwsl --install\\n
\\n\\n\\n\\nЭто позволит загрузить и установить последнюю версию WSL на вашу систему. По умолчанию будет установлен Ubuntu вместе с wsl2.
\\n\\n\\n\\nЕсли вы хотите установить WSL с определенным дистрибутивом Linux, вы можете использовать следующую команду:
\\n\\n\\n\\nwsl --install -d DISTRO-NAME\\n
\\n\\n\\n\\nЗамените “DISTRO-NAME” на имя дистрибутива Linux, который вы хотите установить. Например, чтобы установить Debian, вы должны использовать:
\\n\\n\\n\\nwsl --install -d Debian\\n
\\n\\n\\n\\nПосле установки wsl перезагрузите систему и запустите WSL, нажав на значок дистрибутива Linux в вашей системе или просто набрав wsl в cmd или powershell. Это запустит среду linux в вашей системе windows и попросит вас создать имя пользователя и пароль для wsl.
\\n\\n\\n\\nЧтобы просмотреть список доступных дистрибутивов Linux, которые вы можете установить, используйте следующую команду:
\\n\\n\\n\\nwsl --list --online\\n
\\n\\n\\n\\nЧтобы просмотреть список установленных в системе дистрибутивов Linux, используйте следующую команду:
\\n\\n\\n\\nwsl --list --verbose\\n
\\n\\n\\n\\nВы также можете использовать следующую команду для получения списка установленных дистрибутивов:
\\n\\n\\n\\nwsl -l -v\\n
\\n\\n\\n\\nПервое имя в списке – это дистрибутив по умолчанию, который WSL будет использовать, если вы не укажете имя дистрибутива.
\\n\\n\\n\\nЧтобы изменить дистрибутив Linux по умолчанию, выполните следующую команду:
\\n\\n\\n\\nwsl --set-default DISTRO-NAME\\n
\\n\\n\\n\\nЗамените “DISTRO-NAME” на имя дистрибутива Linux, который вы хотите установить по умолчанию.
\\n\\n\\n\\nЧтобы обновить установку WSL, выполните следующую команду:
\\n\\n\\n\\nwsl --update\\n
\\n\\n\\n\\nЕсли вы хотите удалить какой-либо дистрибутив WSL, сначала снимите его с регистрации в системе. Чтобы снять с регистрации дистрибутив Linux, выполните следующую команду:
\\n\\n\\n\\nwsl --unregister DISTRO-NAME\\n
\\n\\n\\n\\nВ заключение, знание этих основных команд WSL поможет вам начать работу по установке и управлению WSL на машине Windows. С помощью WSL вы сможете воспользоваться преимуществами экосистем Windows и Linux, что делает ее мощным инструментом для разработчиков.
\\n\\n\\n\\r\\nЧто такое WSL? WSL расшифровывается как Windows Subsystem for Linux. Это функция в Windows 10 и windows 11, которая позволяет разработчикам запускать дистрибутивы Linux на своих машинах Windows без необходимости использования виртуальной машины или двойной загрузки. Это означает, что разработчики могут использовать инструменты и приложения Linux на машине Windows без необходимости переключаться между операционными системами […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Как закодить\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/kak-zakodit/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/chto-takoe-wsl-kak-ustanovit-wsl-v-windows-11/\"],\"title\":[0,\"Что такое WSL? Как установить WSL в Windows 11? - Gorlov.\"],\"breadcrumbTitle\":[0,\"Что такое WSL? Как установить WSL в Windows 11?\"],\"description\":[0,\"WSL расшифровывается как Windows Subsystem for Linux. Это функция в Windows 10 и windows 11, которая позволяет разработчикам запускать дистрибутивы Linux на\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"WSL расшифровывается как Windows Subsystem for Linux. Это функция в Windows 10 и windows 11, которая позволяет разработчикам запускать дистрибутивы Linux на\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Что такое WSL? Как установить WSL в Windows 11? - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:45:37+00:00\"],\"url\":[0,\"http://igorlov.loc/chto-takoe-wsl-kak-ustanovit-wsl-v-windows-11/\"]}]}],\"title\":[0,\"Что такое WSL? Как установить WSL в Windows 11?\"],\"uri\":[0,\"/chto-takoe-wsl-kak-ustanovit-wsl-v-windows-11/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"chto-takoe-wsl-kak-ustanovit-wsl-v-windows-11\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Wsl\\\"],\\\"id\\\":[0,\\\"dGVybToyMjI=\\\"],\\\"uri\\\":[0,\\\"/tag/wsl/\\\"]}],[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"id\\\":[0,\\\"dGVybToyNg==\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjEyMTQx\"],\"node\":[0,{\"date\":[0,\"2023-04-30T09:32:17\"],\"content\":[0,\"\\nПредставьте себе следующее: вы впервые создаете приложение React – вы рисуете каркас и пытаетесь представить себе, как все страницы и компоненты будут сочетаться друг с другом, и понимаете, что некоторые из них должны нести немного большую нагрузку, чем другие. В то время как некоторые компоненты должны только отображать свои конкретные компоненты, другие могут потребовать немного больше функциональности, может быть, они получают данные, обновляют DOM или управляют таймером; в общем, если компонент делает что-то, кроме простого возврата части JSX, он выполняет побочные эффекты. К счастью, в React есть специальный хук для таких задач: useEffect().
\\n\\n\\n\\nДля демонстрации давайте создадим компонент, который подсчитывает клики.
\\n\\n\\n\\nДля начала вы вызовете useEffect() почти так же, как и вызов state в верхней части компонента:
\\n\\n\\n\\nimport React, { useState, useEffect } from 'react';
\\n\\n\\n\\nДалее мы создадим остальную часть компонента, направляя ему именно то, что мы хотим, чтобы он производил:
\\n\\n\\n\\nfunction ExampleComponent() {\\n const [count, setCount] = useState(0);\\n\\n useEffect(() => {\\n // This function will be called after every render\\n document.title = `You clicked ${count} times`;\\n });\\n\\n return (\\n <div>\\n <p>You clicked {count} times.</p>\\n <button onClick={() => setCount(count + 1)}>Click me</button>\\n </div>\\n );\\n}\\n
\\n\\n\\n\\nВ приведенном выше примере наш компонент отображает счетчик, а также кнопку. При нажатии состояние кнопки увеличивается через ‘count’, а хук useEffect() при каждом рендере обновляет заголовок документа текущим значением count.
\\n\\n\\n\\nКак вы видите, при вызове useEffect() в качестве основного аргумента принимается функция (которая вызывается после каждого рендеринга) и соответствующим образом обновляет свойство ‘document.title’.
\\n\\n\\n\\nОбратите внимание, что у хука useEffect() нет дополнительных аргументов, поэтому он будет вызываться после каждого рендера. Если мы хотим, чтобы он вызывался только при изменении переменной ‘count’, мы можем просто передать ее в качестве второго аргумента, как показано ниже:
\\n\\n\\n\\nuseEffect(() => {\\n document.title = `You clicked ${count} times!`;\\n}, [count]);\\n
\\n\\n\\n\\nЭто называется зависимостью, которая предотвращает бесконечный цикл перебора аргументов в коде. И хотя у нас есть возможность передавать зависимости через массивы, как мы сделали выше, мы также можем передавать пустые массивы через наши функции в качестве вторых аргументов, как показано ниже:
\\n\\n\\n\\nimport React, { useState, useEffect } from 'react';\\n\\nfunction ExampleComponent() {\\n const [data, setData] = useState([]);\\n\\n useEffect(() => {\\n // This function will be called once on component mount\\n fetch('https://example.com/data')\\n .then(response => response.json())\\n .then(data => setData(data))\\n }, []);\\n\\n return (\\n <ul>\\n {data.map(item => (\\n <li key={item.id}>{item.title}</li>\\n ))}\\n </ul>\\n );\\n
\\n\\n\\n\\nИспользуя пустой массив в качестве второго аргумента, мы сообщаем нашему компоненту, что хотим, чтобы побочный эффект выполнялся только при первом отображении нашего компонента, что еще раз предотвращает бесконечный цикл.
\\n\\n\\n\\nНе знаете, когда и как передавать зависимости через второй аргумент useEffect? Вот краткое руководство!
\\n\\n\\n\\nХотя это лишь краткий обзор, хук useEffect() является бесценным инструментом, когда речь идет о том, чтобы сделать компоненты более динамичными и интерактивными, поскольку он позволяет им реагировать на изменения состояния, взаимодействие с пользователем или даже внешние события. Используя этот хук для управления побочными эффектами, можно оптимизировать функциональность ваших компонентов для обновления в реальном времени, взаимодействия с внешними источниками данных, управления анимацией и переходами и многого другого! Я надеюсь, что вы будете практиковать использование этого метода в своих приложениях React, чтобы понять, насколько мощными могут стать отдельные компоненты. Счастливого кодинга!
\\n\\n\\n\\r\\nПредставьте себе следующее: вы впервые создаете приложение React – вы рисуете каркас и пытаетесь представить себе, как все страницы и компоненты будут сочетаться друг с другом, и понимаете, что некоторые из них должны нести немного большую нагрузку, чем другие. В то время как некоторые компоненты должны только отображать свои конкретные компоненты, другие могут потребовать немного […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Как закодить\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/kak-zakodit/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/useeffect-povyshenie-upravlyaemosti-pobochnyh-effektov-v-prilozheniyah-react-s-2019-goda/\"],\"title\":[0,\"UseEffect(): Повышение управляемости побочных эффектов в приложениях React с 2019 года - Gorlov.\"],\"breadcrumbTitle\":[0,\"useEffect(): Повышение управляемости побочных эффектов в приложениях React с 2019 года\"],\"description\":[0,\"Представьте себе следующее: вы впервые создаете приложение React - вы рисуете каркас и пытаетесь представить себе, как все страницы и компоненты будут\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Представьте себе следующее: вы впервые создаете приложение React - вы рисуете каркас и пытаетесь представить себе, как все страницы и компоненты будут\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"UseEffect(): Повышение управляемости побочных эффектов в приложениях React с 2019 года - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:45:46+00:00\"],\"url\":[0,\"http://igorlov.loc/useeffect-povyshenie-upravlyaemosti-pobochnyh-effektov-v-prilozheniyah-react-s-2019-goda/\"]}]}],\"title\":[0,\"useEffect(): Повышение управляемости побочных эффектов в приложениях React с 2019 года\"],\"uri\":[0,\"/useeffect-povyshenie-upravlyaemosti-pobochnyh-effektov-v-prilozheniyah-react-s-2019-goda/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"useeffect-povyshenie-upravlyaemosti-pobochnyh-effektov-v-prilozheniyah-react-s-2019-goda\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"React\\\"],\\\"id\\\":[0,\\\"dGVybTozNg==\\\"],\\\"uri\\\":[0,\\\"/tag/react/\\\"]}],[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"id\\\":[0,\\\"dGVybToyNg==\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExNTcy\"],\"node\":[0,{\"date\":[0,\"2023-04-27T22:51:42\"],\"content\":[0,\"\\nЧтобы проверить, хорошо ли ваш сайт работает в области SEO и ранжирования, необходимо знать, как работает Google Search Console и какие данные он может вам предоставить, и в этой статье мы расскажем вам обо всем этом.
\\n\\n\\n\\nGoogle предлагает бесплатный сервис Google Search Console, который помогает отслеживать, поддерживать и устранять неполадки, связанные с присутствием сайта в результатах поиска Google. Вам не нужно регистрироваться, чтобы попасть в результаты поиска. Скорее, Search Console помогает понять и улучшить то, как Google воспринимает ваш сайт.
\\n\\n\\n\\nЧто касается SEO и ранжирования, Google Search Console является одним из самых ценных инструментов. Следующая статья посвящена Google Search Console и ее важным метрикам, в ней вы найдете более глубокое понимание всех ключевых показателей, что даст вам понимание того, как их можно использовать.
\\n\\n\\n\\nGoogle Search Console – это набор инструментов, который помогает отслеживать работу сайта, находить проблемы и повышать рейтинг сайта. Этот инструмент является мощным, но в то же время сложным. Этот инструмент является бесценным ресурсом для маркетологов, поскольку он предлагает помощь в обеспечении высокой эффективности веб-сайтов.
\\n\\n\\n\\nПомимо статистических данных и информации, которые он предлагает, инструмент также предоставляет веб-мастерам инструменты для устранения проблем, которые могут помешать успешной работе сайта. Search Console, в отличие от Google Analytics, предоставляет информацию о трафике, который поступает только из веб-поиска, а не из других сегментов, таких как трафик из рекламы, прямой трафик и так далее.
\\n\\n\\n\\nВ консоли поиска Google доступны несколько метрик для измерения производительности сайта и помощи веб-мастерам в анализе проблем сайта. В этом блоге вы найдете самые важные метрики консоли поиска Google.
\\n\\n\\n\\nРезультаты поиска можно найти на левой боковой панели. Раздел дает представление о том, как сайт выглядит на странице результатов поисковой системы. Сюда входят данные о показах, кликах, количестве переходов, позиции и о том, по каким запросам показывается сайт.
\\n\\n\\n\\nВерхние фильтры позволяют сортировать данные на основе местоположения, вида поиска, данных и так далее. Эти данные имеют первостепенное значение, когда речь идет о понимании влияния усилий SEO.
\\n\\n\\n\\nЛюбой клик для большинства типов результатов, который направляет пользователя на страницу вне Google Поиск, Новости или Discover, считается кликом. Помните, что щелчок по ссылке внутри Google не считается щелчком.
\\n\\n\\n\\nВпечатления означают, что пользователь видел или, возможно, видел ссылку на сайт в Discover, Search или News. Впечатление в целом учитывается каждый раз, когда элемент появляется на текущей странице результатов, независимо от того, прокручивается ли он, при условии, что пользователь не нажимает кнопку мыши для просмотра других результатов. Прокрутка вперед и назад в течение одного сеанса или запроса не считается многократным впечатлением.
\\n\\n\\n\\n
Коэффициент кликов — это количество пользователей, которые кликнули на результат и перешли на сайт. Он рассчитывается как количество кликов, деленное на количество показов, умноженное на сто и представленное в процентах.
Позиция рассчитывается только для результатов поиска Google. Страница результатов состоит из нескольких элементов результатов. Как правило, позиция в Google Поиске рассчитывается сверху вниз на стороне первичной страницы, затем сверху вниз на вторичной странице.
\\n\\n\\n\\nИнструмент URL Inspection предоставляет информацию об индексированной версии определенной страницы в Google. Кроме того, он позволяет проверить, может ли URL быть индексируемым. Информация включает сведения о видео, структурированных данных, индексации/индексируемости и связанном AMP.
\\n\\n\\n\\nДоступ к инструменту осуществляется двумя способами:
\\n\\n\\n\\nВвод полностью квалифицированного URL-адреса для проверки в строке поиска в верхней части любого экрана Search Console.
\\n\\n\\n\\nЩелкните и проверьте ссылку рядом с URL-адресом страницы в большинстве отчетов.
\\n\\n\\n\\nURL перенаправляет на другой URL и поэтому не был проиндексирован. Конечная цель URL может быть проиндексирована и должна появиться в отчете. Количество проиндексированных URL можно увидеть в сводке Индексирование страниц.
\\n\\n\\n\\n
Когда вы видите “страницу с перенаправлением” в отчете о покрытии, это означает, что Google нашел на сайте страницу с перенаправленным URL и не стал ее индексировать. Это происходит для того, чтобы избежать дублирования результатов, или для обнаружения ошибок.
Это означает, что Google проиндексировал URL, даже если файл robots.txt заблокировал его. Поскольку Google не уверен, хотите ли вы, чтобы эти URL индексировались, он покажет предупреждение для них.
Это означает, что Google обнаружил в XML sitemap URL, помеченные как noindex. Это приводит к тому, что Google не индексирует их, поскольку они часто следуют директивам robots.
Карта сайта – это файл, в котором вы предоставляете информацию о страницах, видео, а также других файлах на сайте и их взаимосвязи. Карта сайта подскажет Google, какие страницы и файлы вы считаете важными для вашего сайта, а также предоставит ценную информацию о них.
\\n\\n\\n\\nКарта сайта может содержать информацию о конкретных типах содержимого на страницах, включая изображения, видео и новостные материалы.
\\n\\n\\n\\nПример URL-адреса карты сайта: https://example.com/sitemap.xml
\\n\\n\\n\\nВнутри XML-файла sitemap вы увидите следующую информацию.
\\n\\n\\n\\n<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n<urlset xmlns=\\\"http://www.sitemaps.org/schemas/sitemap/0.9\\\">\\n <url>\\n <loc>https://www.example.com/foo.html</loc>\\n <lastmod>2022-06-04</lastmod>\\n </url>\\n</urlset>
\\n\\n\\n\\nЭтот инструмент позволяет временно блокировать страницы из результатов поиска Google на принадлежащих вам сайтах. Кроме того, с его помощью можно просмотреть историю запросов на удаление страниц от владельцев и не владельцев. Он также позволяет увидеть все URL-адреса, о которых сообщалось, что они содержат материалы для взрослых на вашем сайте.
\\n\\n\\n\\nОтчет Core Web Vitals показывает, как работают страницы, основываясь на реальном использовании данных. Узнайте больше о различных аспектах основных веб-показателей ниже.
\\n\\n\\n\\n
Это количество времени на отрисовку самого большого элемента, видимого в области просмотра с момента запроса URL пользователем. Обычно это видео, изображение или даже большой текстовый элемент на уровне блока.
Время, в течение которого пользователь взаимодействует со страницей – от щелчка по ссылке или нажатия кнопки до момента, когда браузер реагирует на это взаимодействие. Это важно для страниц, на которых пользователям нужно что-то сделать, поскольку именно в этот момент страница становится интерактивной.
Измеряет сумму всех индивидуальных оценок сдвига макета для каждого неожиданного сдвига макета, произошедшего за все время существования страницы. Сумма баллов варьируется от нуля до любого положительного числа. Ноль означает отсутствие сдвига, а большее число указывает на большее количество сдвигов макета на странице. CLS имеет большое значение, поскольку смещение элементов страницы в тот момент, когда пользователь пытается взаимодействовать с ней, не очень приятно для пользователя.
Помогает исправить ошибки, которые мешают вашим AMP-страницам появляться в результатах поиска Google, с помощью функций, специфичных для AMP. Вид на верхнем уровне показывает критические проблемы, которые влияют на AMP-страницы сайта.
\\n\\n\\n\\nОтчет “Мобильное удобство” показывает страницы с проблемами удобства использования при просмотре на мобильных устройствах. Нажмите на конкретную проблему, чтобы увидеть подробности проблемы, например, примерный список страниц, затронутых проблемой, информацию о ее устранении и процесс уведомления Google о внесенных исправлениях.
\\n\\n\\n\\nСсылки помогают увидеть, кто больше всего ссылается на ваш сайт, ваши страницы с наибольшим количеством ссылок и т.д.
\\n\\n\\n\\nРучные действия предпринимаются Google в отношении веб-сайта, когда человеческий эксперт определяет, что страницы сайта не соответствуют рекомендациям Google по качеству для веб-мастеров. Многие ручные действия связаны с попытками манипулирования поисковым индексом. Кроме того, большинство проблем, о которых здесь сообщается, приводят к тому, что сайты или страницы занимают более низкие позиции или даже исключаются из результатов поиска.
\\n\\n\\n\\nОтчет с ручным действием
\\n\\n\\n\\nОтчет отображает статистику, касающуюся истории ползания по вашему сайту в Google. Например, вы можете увидеть, сколько запросов было сделано и когда, ответ сервера и наличие возникших проблем.
\\n\\n\\n\\nОтчет можно использовать для определения того, сталкивается ли Google с проблемами обслуживания при просмотре вашего сайта. Отчет предназначен для опытных пользователей. Для страниц, на которых менее тысячи страниц, этот отчет не нужен.
\\n\\n\\n\\nОтчет Google Search Console позволяет получить более глубокое представление о работе вашего сайта. А также с помощью ключевых показателей, упомянутых выше, вы сможете устранить проблемы SEO вашего сайта, что в конечном итоге поможет вашему сайту добиться хороших результатов в поисковой системе Google.
\\n\\n\\n\\r\\nЧтобы проверить, хорошо ли ваш сайт работает в области SEO и ранжирования, необходимо знать, как работает Google Search Console и какие данные он может вам предоставить, и в этой статье мы расскажем вам обо всем этом. Google предлагает бесплатный сервис Google Search Console, который помогает отслеживать, поддерживать и устранять неполадки, связанные с присутствием сайта в […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/key-metrics-of-google-search-console/\"],\"title\":[0,\"Ключевые метрики Google Search Console - Gorlov.\"],\"breadcrumbTitle\":[0,\"Ключевые метрики Google Search Console\"],\"description\":[0,\"Чтобы проверить, хорошо ли ваш сайт работает в области SEO и ранжирования, необходимо знать, как работает Google Search Console и какие данные он может вам\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Чтобы проверить, хорошо ли ваш сайт работает в области SEO и ранжирования, необходимо знать, как работает Google Search Console и какие данные он может вам\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Ключевые метрики Google Search Console - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:45:58+00:00\"],\"url\":[0,\"http://igorlov.loc/key-metrics-of-google-search-console/\"]}]}],\"title\":[0,\"Ключевые метрики Google Search Console\"],\"uri\":[0,\"/key-metrics-of-google-search-console/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"key-metrics-of-google-search-console\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Search console\\\"],\\\"id\\\":[0,\\\"dGVybToyMjE=\\\"],\\\"uri\\\":[0,\\\"/tag/search-console/\\\"]}],[0,{\\\"name\\\":[0,\\\"SEO\\\"],\\\"id\\\":[0,\\\"dGVybToyMjA=\\\"],\\\"uri\\\":[0,\\\"/tag/seo/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExNTcw\"],\"node\":[0,{\"date\":[0,\"2023-04-27T21:56:07\"],\"content\":[0,\"\\nВ этой статье мы рассмотрим атаки Cross-Site Request Forgery (CSRF) в контексте приложения Next.js и способы защиты от них. Сначала мы рассмотрим концепцию CSRF-атак и то, как они могут повлиять на веб-приложение в целом. Для этого мы опишем сценарий, в котором мы запустим CSRF-атаку на наше приложение Next.js. Затем мы используем пакет next-csrf и определенные теги безопасности cookie, чтобы показать, как защититься от этих атак. Исходный код этой заметки можно найти здесь.
\\n\\n\\n\\nПредставьте, что вы вошли на сайт онлайн-банкинга, который устанавливает cookie в вашем браузере для поддержания вашей личной сессии. Этот файл cookie содержит маркер аутентификации, который используется для идентификации вашей сессии и аутентификации ваших запросов.
\\n\\n\\n\\nОчень упрощенный, но возможный HTTP-запрос для отправки денег может быть следующим:
\\n\\n\\n\\nPOST /transfer HTTP/1.1\\nHost: vulnerable-bank.com\\nContent-Type: application/json\\nContent-Length: 30\\nCookie: session=454544\\n\\namount=1000$\\nname=friendlyuser@gmail.com\\niban=DE7823778237873
\\n\\n\\n\\nВ то же время при CSRF-атаке у вас в браузере открыта еще одна вкладка, на которой загружен вредоносный сайт. Этот вредоносный сайт может содержать скрытую форму или код JavaScript, который отправляет запрос на сайт онлайн-банка, используя наш маркер аутентификации.
\\n\\n\\n\\nПоскольку запрос инициируется из одного и того же браузера, веб-приложение не может отличить законный запрос, инициированный нами, от поддельного запроса, отправленного злоумышленником. Веб-приложение обработает запрос и выполнит непредусмотренное действие без нашего ведома или согласия.
\\n\\n\\n\\nВ этом разделе мы обсудим несколько различных способов защиты от CSRF-атак.
\\n\\n\\n\\nВозможный способ защитить ваше приложение Next.js от CSRF-атак – это определить значение SameSite внутри файлов cookie, которые вы используете на своем сайте. Google ввел это значение в 2006 году с целью предотвратить автоматическую отправку файлов cookie вместе с межсайтовыми запросами браузера, как это происходило ранее, что позволило бы минимизировать риск потери конфиденциальной информации и обеспечить защиту от подделки межсайтовых запросов.
\\n\\n\\n\\nАтрибут SameSite может принимать значение strict или lax. В строгом режиме защищенный файл cookie не отправляется ни с одним межсайтовым запросом. Это уже применимо к щелчку на простой ссылке, но если применить это к нашему примеру с онлайн-банкингом, то это означает, что вам придется заново проходить аутентификацию каждый раз, когда вы будете перенаправлены на страницу онлайн-банкинга.
\\n\\n\\n\\nЭто не соответствует обычному поведению веб-приложений, поскольку пользователи не хотят постоянно заново входить в систему. К счастью, режим lax несколько смягчает такое поведение и позволяет отправлять cookie вместе с некоторыми “безопасными” межсайтовыми запросами. Это влияет только на безопасные методы HTTP, доступные только для чтения, и навигацию верхнего уровня (действия, которые приводят к изменению URL в адресной строке браузера, например, ссылки).
\\n\\n\\n\\nНиже приведен обзор различных типов запросов и их различных вариантов, которые влияют на то, будет ли отправлен файл cookie или нет. Большой палец вверх означает, что cookie будет отправлен. Например, вы можете видеть, что в строгом режиме cookie никогда не будет отправлен вместе с межсайтовым запросом.
\\n\\n\\n\\nТип запроса | Пример | Без SameSite | нестрогий режим | строгий режим |
a-tag | <a href=”..”> | 👍 | 👍 | 👎 |
form (get) | <form method=”get”…> | 👍 | 👍 | 👎 |
form (post) | <form method=”post”…> | 👍 | 👎 | 👎 |
iframe | <iframe src=”..”> | 👍 | 👎 | 👎 |
ajax | $.get(“…”) | 👍 | 👎 | 👎 |
image-tag | <img src=”…”> | 👍 | 👎 | 👎 |
Установив флаг HttpOnly cookie, вы можете снизить вероятность CSRF-атаки, поскольку HTTP-only cookie не могут быть получены JavaScript через сценарии на стороне клиента.
\\n\\n\\n\\nres.setHeader(\\\"Set-Cookie\\\", `session=${sessionId}; Path=/; Max-Age=600; SameSite=Strict; HttpOnly`);\\n
\\n\\n\\n\\nОдним из способов защиты вашего веб-приложения от CSRF-атаки является использование так называемых CSRF-токенов. CSRF-токен – это уникальное случайное значение, которое генерируется на стороне сервера и включается в каждый запрос, отправляемый клиентом. Если маркер, отправленный клиентом, совпадает с маркером, хранящимся на стороне сервера, запрос считается легитимным и обрабатывается сервером. В противном случае запрос будет отклонен.
\\n\\n\\n\\nВажно отметить, что CSRF-токены обеспечивают эффективную защиту от CSRF-атак до тех пор, пока токен генерируется случайным образом и не может быть легко угадан или предсказан. Кроме того, срок действия токена должен истекать через определенный период времени или после однократного использования, чтобы предотвратить повторное использование злоумышленниками старых токенов.
\\n\\n\\n\\nВ этом разделе мы рассмотрим код примера страницы онлайн-банкинга и то, как она уязвима для CSRF-атак. После этого мы реализуем защиту от CSRF с помощью пакета next-csrf и установки значения SameSite в куки сессии.
\\n\\n\\n\\nНаш демонстрационный онлайн-банк состоит из двух основных маршрутов: маршрут входа и маршрут перевода. Маршрут перевода доступен только после успешной аутентификации через маршрут входа. Для этого я создал простой API-маршрут для обработки запроса на вход:
\\n\\n\\n\\n// pages/api/login.js\\n\\nexport default function login(req, res) {\\n // check the user's credentials\\n const { username, password } = req.body;\\n let authenticated;\\n\\n if (username === \\\"test\\\" && password === \\\"123456\\\") {\\n authenticated === true \\n } else {\\n authenticated === false\\n }\\n \\n\\n if (authenticated) {\\n // set a cookie with the a random sessionId\\n const sessionId = 454544;\\n res.setHeader(\\\"Set-Cookie\\\", `session=${sessionId}; Path=/; Max-Age=600`);\\n\\n // send a success response\\n res.status(200).json({ message: \\\"Login successful\\\" });\\n } else {\\n // send an error response\\n res.status(401).json({ message: \\\"Invalid credentials\\\" });\\n }\\n}
\\n\\n\\n\\nСамой важной строкой кода в приведенном выше коде, вероятно, является:
\\n\\n\\n\\nres.setHeader(\\\"Set-Cookie\\\", `session=${sessionId}; Path=/; Max-Age=600`);
\\n\\n\\n\\nЭто устанавливает cookie с идентификатором сессии и продолжительностью 10 минут. Для простоты мы используем жестко закодированные идентификаторы сеанса, имена пользователей и пароли.
\\n\\n\\n\\nПосле успешной аутентификации вы должны увидеть страницу перевода средств на нашем демонстрационном сайте онлайн-банкинга:
\\n\\n\\n\\nСоответствующий упрощенный API-маршрут для обработки банковских переводов выглядит следующим образом:
\\n\\n\\n\\n// pages/api/transfer.js\\n\\nexport default function handler(req, res) {\\n // Check that the request method is POST\\n if (req.method !== 'POST') {\\n res.status(405).json({ error: 'Method Not Allowed' });\\n return;\\n }\\n\\n // Check that the request has a valid session cookie\\n if (!req.cookies.session) {\\n res.status(401).json({ error: 'Unauthorized' });\\n return;\\n }\\n\\n // Parse the JSON data from the request body\\n const { amount, name, iban } = req.body;\\n\\n // TODO: Implement transfer logic\\n\\n // Return a success message\\n res.status(200).json({ message: 'Transfer successful' });\\n}
\\n\\n\\n\\nВ приведенном выше коде мы выполняем две проверки: Одна для метода запроса, а другая проверяет сессионный файл cookie.
\\n\\n\\n\\nМы не собираемся создавать вредоносный сайт; вместо этого мы будем имитировать отправку данных через форму или код JavaScript на этом вредоносном сайте. Поскольку запрос инициируется из того же браузера, к нему автоматически будет прикреплен сеансовый cookie, и наш бэкенд не сможет отличить легитимный запрос, инициированный нами как аутентифицированным пользователем, от поддельного запроса, отправленного злоумышленником.
\\n\\n\\n\\nВсе, что нам нужно отправить через этот CURL-запрос, это данные формы (сумма, имя, iban) и сессионный cookie. Соответствующий запрос выглядит следующим образом:
\\n\\n\\n\\ncurl -X POST \\\\\\n -H \\\"Content-Type: application/x-www-form-urlencoded\\\" \\\\\\n -H \\\"Cookie: session=1234\\\" \\\\\\n -d \\\"iban=1736123125&amount=10000000&name=Criminal\\\" \\\\\\n http://localhost:3000/api/transfer
\\n\\n\\n\\nНезащищенный маршрут api/transfer приведет к такому ответу:
\\n\\n\\n\\n{\\\"name\\\":\\\"Criminal\\\",\\\"iban\\\":\\\"1736123125\\\",\\\"amount\\\":\\\"10000000\\\"}
\\n\\n\\n\\nЭтот ответ означает, что мы только что успешно выполнили CSRF-атаку на странице онлайн-банкинга.
\\n\\n\\n\\nДавайте сначала реализуем атрибуты SameSite и HttpOnly нашего сессионного cookie, поскольку это легко сделать за один шаг. Помните, что мы установили cookie в нашем маршруте API входа в систему, расположенном в src/pages/api/login.js. Давайте настроим параметр cookie в соответствующем маршруте:
\\n\\n\\n\\nres.setHeader(\\\"Set-Cookie\\\", `session=${sessionId}; Path=/; Max-Age=600; SameSite=Strict; HttpOnly`);\\n
\\n\\n\\n\\nЭто все, что вам нужно сделать, чтобы настроить сессионный файл cookie. Выбор строгой или мягкой политики зависит от того, насколько высоки ваши требования к безопасности и чем вы готовы пожертвовать в плане пользовательского опыта.
\\n\\n\\n\\nКак уже упоминалось в разделах выше, существует пакет next-csrf, который позволяет легко реализовать следующие шаги для обеспечения защиты от CSRF-атак:
\\n\\n\\n\\nДля успешного проведения атаки CSRF злоумышленнику необходимо получить маркер CSRF с вашего сайта и использовать JavaScript для доступа к нему. Это означает, что если на вашем сайте не разрешен кросс-оригинальный обмен ресурсами (CORS), злоумышленник не сможет получить доступ к маркеру CSRF, что эффективно нейтрализует угрозу.
\\n\\n\\n\\nЧтобы установить пакет next-csrf, выполните следующую команду в корне вашего проекта Next.js:
\\n\\n\\n\\nnpm i next-csrf --save
\\n\\n\\n\\nНа первом этапе давайте инициализируем next-csrf, создав установочный файл. Это создаст промежуточное программное обеспечение для создания и проверки CSRF-токенов:
\\n\\n\\n\\n// \\\"lib/csrf\\\"\\nimport { nextCsrf } from \\\"next-csrf\\\";\\n\\nconst { csrf, setup } = nextCsrf({\\n // eslint-disable-next-line no-undef\\n secret: \\\"12345\\\",\\n});\\n\\nexport { csrf, setup };
\\n\\n\\n\\nВ производственной среде вы, конечно же, будете хранить свой секрет в файле окружения.
\\n\\n\\n\\nЧтобы установить CSRF-токен, мы будем использовать страницу с рендерингом на стороне сервера, как наша страница входа в систему, потому что вы используете смягчение CSRF для защиты ваших запросов от аутентифицированных пользователей.
\\n\\n\\n\\nimport Head from \\\"next/head\\\";\\nimport { setup } from \\\"lib/csrf\\\";\\n\\nexport default function Home() {\\n return (\\n ...\\n );\\n}\\n\\nexport const getServerSideProps = setup(async ({ req, res }) => {\\n return {\\n props: {},\\n };\\n});
\\n\\n\\n\\nПосле этого единственное, что нам нужно сделать для защиты маршрута API, — это обернуть соответствующий маршрут API нашим промежуточным программным обеспечением csrf:
\\n\\n\\n\\n// src/pages/api/transfer.js\\nimport { csrf } from \\\"../../../lib/csrf\\\";\\n\\nconst handler = (req, res) => {\\n // Check that the request method is POST\\n if (req.method !== 'POST') {\\n res.status(405).json({ error: 'Method Not Allowed' });\\n return;\\n }\\n // Check that the request has a valid session cookie\\n if (!req.cookies.session) {\\n res.status(401).json({ error: 'Unauthorized' });\\n return;\\n }\\n // Parse the JSON data from the request body\\n const { name, iban, amount } = req.body;\\n console.log(name, iban, amount)\\n console.log(req.cookies.session);\\n // Return a success message\\n res.status(200).json({ name, iban, amount });\\n }\\n\\n export default csrf(handler);
\\n\\n\\n\\nПеред выполнением логики запроса промежуточное ПО csrf выполнит проверку CSRF-токенов и в случае неудачной проверки выдаст ошибку:
\\n\\n\\n\\n{\\\"message\\\":\\\"Invalid CSRF token\\\"}
\\n\\n\\n\\nВ этой статье мы рассмотрели тему защиты вашего приложения Next.js от CSRF-атак и подробно рассмотрели пакет next-csrf, который позволяет реализовать защиту от CSRF с помощью CSRF-токенов. Кроме того, мы рассмотрели конфигурацию cookie-файлов и способы повышения безопасности путем установки определенных значений cookie-файлов.
\\n\\n\\n\\r\\nВ этой статье мы рассмотрим атаки Cross-Site Request Forgery (CSRF) в контексте приложения Next.js и способы защиты от них. Сначала мы рассмотрим концепцию CSRF-атак и то, как они могут повлиять на веб-приложение в целом. Для этого мы опишем сценарий, в котором мы запустим CSRF-атаку на наше приложение Next.js. Затем мы используем пакет next-csrf и определенные […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/zashhita-prilozhenij-next-js-ot-csrf-atak/\"],\"title\":[0,\"Защита приложений Next.js от CSRF-атак - Gorlov.\"],\"breadcrumbTitle\":[0,\"Защита приложений Next.js от CSRF-атак\"],\"description\":[0,\"В этой статье мы рассмотрим атаки Cross-Site Request Forgery (CSRF) в контексте приложения Next.js и способы защиты от них. Сначала мы рассмотрим концепцию\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"В этой статье мы рассмотрим атаки Cross-Site Request Forgery (CSRF) в контексте приложения Next.js и способы защиты от них. Сначала мы рассмотрим концепцию\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Защита приложений Next.js от CSRF-атак - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:46:08+00:00\"],\"url\":[0,\"http://igorlov.loc/zashhita-prilozhenij-next-js-ot-csrf-atak/\"]}]}],\"title\":[0,\"Защита приложений Next.js от CSRF-атак\"],\"uri\":[0,\"/zashhita-prilozhenij-next-js-ot-csrf-atak/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"zashhita-prilozhenij-next-js-ot-csrf-atak\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Next.js\\\"],\\\"id\\\":[0,\\\"dGVybToxNTA=\\\"],\\\"uri\\\":[0,\\\"/tag/next-js/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExNTY4\"],\"node\":[0,{\"date\":[0,\"2023-04-27T21:42:29\"],\"content\":[0,\"\\nСигналы в django – это способ для ваших моделей общаться друг с другом при наступлении события. Те, кто работал с JavaScript, скорее всего, сталкивались с методом .addEventListener(), который вызывается только при наступлении события. Этот метод работает так же, как и сигналы django, но на этот раз он вызывается, когда одна часть двух связанных моделей сохраняется, удаляется, обновляется и т.д.
\\n\\n\\n\\nНиже перечислены некоторые из доступных событий, которые могут быть вызваны.
\\n\\n\\n\\nОдна очень важная часть моего django приложения, где я использую сигналы, это когда я работаю с моделью, которая имеет отношения один к одному с моделью пользователя. В этом учебнике я буду использовать модель Profile. У каждого пользователя должен быть профиль, который является расширением модели пользователя. На самом деле это можно сделать разными способами, один из которых – переопределить метод сохранения модели пользователя и затем создать профиль для созданного пользователя. Но давайте предположим, что мы хотим, чтобы все было просто и аккуратно, сигналы будут правильным способом.
\\n\\n\\n\\nЯ буду считать, что вы уже знаете, как начать проект django, поэтому я пропущу эту часть. Перейдите в файл models.py вашего приложения и создайте модель Profile.
\\n\\n\\n\\nfrom django.contrib.auth import get_user_model\\nimport uuid\\n\\n# user object\\nUser = get_user_model()\\n\\n\\nclass Profile(models.Model):\\n id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)\\n user = models.OneToOneField(User, on_delete=models.CASCADE)\\n friends = models.ManyToManyField(\\\"self\\\", blank=True)\\n picture = models.ImageField(upload_to=\\\"dps\\\")\\n\\n def __str__(self):\\n return self.user.username\\n\\n\\n
\\n\\n\\n\\nХорошо! Мы настроили нашу модель профиля, давайте сделаем миграции и мигрируем в базу данных
\\n\\n\\n\\n$ python manage.py makemigrations\\n$ python manage.py migrate\\n
\\n\\n\\n\\nТеперь мы хотим, чтобы профили автоматически создавались при создании экземпляра пользователя. Для этого создайте файл в каталоге вашего приложения и назовите его signals.py
\\n\\n\\n\\n#signals.py\\n\\nfrom django.db.models.signals import post_save\\nfrom django.dispatch import receiver\\nfrom .models import Profile, User\\n\\n@receiver(post_save, sender=User)\\ndef create_profile(sender, instance, created, **kwargs):\\n if created:\\n Profile.objects.create(user=instance)\\n print(f\\\"Profile for {instance.username} created successfully!!\\\")\\n\\n
\\n\\n\\n\\nЗдесь я использовал сигнал post_save, потому что наш профиль будет создан после успешного создания пользователя.
\\n\\n\\n\\nНам нужно сообщить нашему приложению об этом файле, поэтому перейдите к вашему файлу apps.py, который был создан, когда вы выполнили $ python manage.py startapp , и добавьте в него этот фрагмент кода.
\\n\\n\\n\\nclass AccountConfig(AppConfig):\\n default_auto_field = 'django.db.models.BigAutoField'\\n name=\\\"account\\\"\\n\\n # add this\\n def ready(self):\\n from . import signals\\n
\\n\\n\\n\\nТеперь все готово. Чтобы это работало, убедитесь, что ваше приложение зарегистрировано в INSTALLED_APPS как <имяприложения>.apps.<имяприложения>Config.
\\n\\n\\n\\nТеперь вы можете создавать экземпляры профилей просто на лету. Спасибо, что прочитали, оставляйте комментарии ниже. До встречи в следующем выпуске.
\\n\\n\\n\\r\\nДобрый день, гики!!! Сигналы в django – это способ для ваших моделей общаться друг с другом при наступлении события. Те, кто работал с JavaScript, скорее всего, сталкивались с методом .addEventListener(), который вызывается только при наступлении события. Этот метод работает так же, как и сигналы django, но на этот раз он вызывается, когда одна часть двух […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Как закодить\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/kak-zakodit/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/django-signaly/\"],\"title\":[0,\"Django сигналы - Gorlov.\"],\"breadcrumbTitle\":[0,\"Django сигналы\"],\"description\":[0,\"Сигналы в django - это способ для ваших моделей общаться друг с другом при наступлении события. Те, кто работал с JavaScript, скорее всего, сталкивались с\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Сигналы в django - это способ для ваших моделей общаться друг с другом при наступлении события. Те, кто работал с JavaScript, скорее всего, сталкивались с\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Django сигналы - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:46:20+00:00\"],\"url\":[0,\"http://igorlov.loc/django-signaly/\"]}]}],\"title\":[0,\"Django сигналы\"],\"uri\":[0,\"/django-signaly/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"django-signaly\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Django\\\"],\\\"id\\\":[0,\\\"dGVybToxNjA=\\\"],\\\"uri\\\":[0,\\\"/tag/django/\\\"]}],[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"id\\\":[0,\\\"dGVybToyNg==\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExNTcz\"],\"node\":[0,{\"date\":[0,\"2023-04-27T21:36:29\"],\"content\":[0,\"\\nCertifyWP запустила свой первый экзамен на получение сертификата WordPress Management and Design Credential. Некоммерческая организация была основана Талишей Леваллен с целью помочь соискателям лучше продемонстрировать свои навыки потенциальным работодателям и дать компаниям возможность понять навыки потенциальных сотрудников.
\\n\\n\\n\\nНовый диплом стоит $150 за экзамен и включает в себя три курса по освоению фронтенд-разработки:
\\n\\n\\n\\nWordPress Management and Design Обладатели диплома подтвердили свое понимание важных аспектов WordPress и веб-сайтов на его основе. Этот сертификат предназначен для всех, кто хочет работать в сфере WordPress или научиться создавать веб-сайты WordPress. Получив сертификат, эти люди готовы к работе в области веб-дизайна WordPress, электронной коммерции, JavaScript, баз данных и других областях.
\\n\\n\\n\\nМногие пункты, относящиеся к начальному и среднему уровням сертификата, по сути, описывают “опытного пользователя” WordPress, или человека, который знает толк в платформе и ее более продвинутых возможностях, включая такие навыки, как навигация по панели администратора, хостинг, веб-оптимизация и многое другое.
\\n\\n\\n\\nCertifyWP проводит прокторированный экзамен для получения этой квалификации, и те, кто соответствует требованиям, должны будут сдавать экзамен каждые три года для поддержания своей квалификации. Экзамен будет обновляться по мере необходимости консультативным советом CertifyWP.
\\n\\n\\n\\nВ экзамене 110 вопросов, распределенных по трем уровням, а также практический компонент. Для получения проходного балла необходимо ответить на вопросы в течение 60 минут, набрав 80%. Экзаменующимся не обязательно проходить курс CertifyWP, чтобы получить право на сдачу экзамена – он открыт для всех желающих.
\\n\\n\\n\\nЛьюаллен также является владельцем WPConnects, компании, которая помогает военным ветеранам пройти обучение, пока они находятся на действительной службе, а затем предоставляет им наставников и помощь в трудоустройстве, когда они увольняются из армии. В недавнем эпизоде подкаста WP Tavern Jukebox она рассказала о том, как эта работа привела ее к созданию CertifyWP с помощью сообщества WordPress.
\\n\\n\\n\\nЛеваллен также выступит на саммите веб-агентств Atarim 25 апреля с докладом о важности сертификатов для WordPress-работников, о различиях между сертификатами и удостоверениями, а также о том, как можно использовать сертификаты при поиске работы.
\\n\\n\\n\\r\\nCertifyWP запустила свой первый экзамен на получение сертификата WordPress Management and Design Credential. Некоммерческая организация была основана Талишей Леваллен с целью помочь соискателям лучше продемонстрировать свои навыки потенциальным работодателям и дать компаниям возможность понять навыки потенциальных сотрудников. Новый диплом стоит $150 за экзамен и включает в себя три курса по освоению фронтенд-разработки: WordPress Management and […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Новости\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/novosti/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/certifywp-zapuskaet-kredencziyu-po-upravleniyu-i-dizajnu-wordpress/\"],\"title\":[0,\"CertifyWP запускает креденцию по управлению и дизайну WordPress - Gorlov.\"],\"breadcrumbTitle\":[0,\"CertifyWP запускает креденцию по управлению и дизайну WordPress\"],\"description\":[0,\"CertifyWP запустила свой первый экзамен на получение сертификата WordPress Management and Design Credential. Некоммерческая организация была основана Талишей\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"CertifyWP запустила свой первый экзамен на получение сертификата WordPress Management and Design Credential. Некоммерческая организация была основана Талишей\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"CertifyWP запускает креденцию по управлению и дизайну WordPress - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:46:33+00:00\"],\"url\":[0,\"http://igorlov.loc/certifywp-zapuskaet-kredencziyu-po-upravleniyu-i-dizajnu-wordpress/\"]}]}],\"title\":[0,\"CertifyWP запускает креденцию по управлению и дизайну WordPress\"],\"uri\":[0,\"/certifywp-zapuskaet-kredencziyu-po-upravleniyu-i-dizajnu-wordpress/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"certifywp-zapuskaet-kredencziyu-po-upravleniyu-i-dizajnu-wordpress\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Новости\\\"],\\\"uri\\\":[0,\\\"/category/novosti/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"WordPress\\\"],\\\"id\\\":[0,\\\"dGVybToyOA==\\\"],\\\"uri\\\":[0,\\\"/tag/wordpress/\\\"]}],[0,{\\\"name\\\":[0,\\\"Новости\\\"],\\\"id\\\":[0,\\\"dGVybTox\\\"],\\\"uri\\\":[0,\\\"/category/novosti/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExNTc5\"],\"node\":[0,{\"date\":[0,\"2023-04-27T21:33:56\"],\"content\":[0,\"\\nuseMemo – это хук React, который позволяет кэшировать результат вычислений между рендерами.
\\n\\n\\n\\nКак правило, useMemo уменьшает объем работы, которую необходимо выполнить при данном рендере. useMemo может мемоизировать функцию и ее результат, что означает, что если входы функции не изменяются, React вернет мемоизированное значение вместо повторного вычисления, что потенциально ускорит процесс рендеринга.
\\n\\n\\n\\nДопустим, у нас есть очень медленная функция, которая занимает огромное количество времени на вычисление в процессе рендеринга:
\\n\\n\\n\\n// a really slow function...\\nconst slowFunction = (num) => {\\n for (let i = 0; i < 1000000000; i++) { }\\n return num * 2;\\n}\\n
\\n\\n\\n\\nИ нам нужен результат этой slowFunction для рендеринга веб-страницы, например, переменная complexResult:
\\n\\n\\n\\nconst complexResult = slowFunction(input);\\n
\\n\\n\\n\\n<p> { complexResult } </p>\\n
\\n\\n\\n\\nВ этом случае вызов slowFunction в каждом рендере значительно замедлит работу вашего приложения. Вот здесь и пригодится useMemo.
\\n\\n\\n\\nМы можем обернуть slowFunction внутри useMemo и предоставить массив зависимостей. Массив зависимостей используется для определения того, нужно ли пересчитывать мемоизированное значение. Если какая-либо из зависимостей изменится, useMemo пересчитает мемоизированное значение, или просто будет использовать предыдущее “мемоизированное” значение.
\\n\\n\\n\\nПростая метафора может быть такой:
\\n\\n\\n\\nВот:
\\n\\n\\n\\nПрототип приведен ниже:
\\n\\n\\n\\nconst cachedValue = useMemo(calculateValue, dependencies);\\n
\\n\\n\\n\\nгде
\\n\\n\\n\\nВернемся к нашему примеру, ранее мы имели:
\\n\\n\\n\\nconst complexResult = slowFunction(input);\\n
\\n\\n\\n\\nА с помощью UseMemo эту строку можно изменить на:
\\n\\n\\n\\nconst complexResult = useMemo(() => {\\n return slowFunction(input)\\n}, [input])\\n
\\n\\n\\n\\nВ приведенном выше примере complexResult будет пересчитан только в том случае, если изменится зависимость ввода. Если входные данные останутся прежними, React вернет ранее мемоизированное значение, что избавит нас от необходимости снова и снова вызывать slowFunction.
\\n\\n\\n\\nЕсли вы все еще считаете эту концепцию абстрактной или вам просто нужно немного контекста для размышлений. Ниже приведен немного более сложный пример.
\\n\\n\\n\\nimport { useState } from \\\"react\\\";\\n\\nconst slowFunction = (num) => {\\n console.log(\\\"running slow double calculation...\\\");\\n for (let i = 0; i < 1000000000; i++) {}\\n return num * 2;\\n};\\n\\nconst Demo = () => {\\n const [number, setNumber] = useState(0);\\n const [color, setColor] = useState(\\\"black\\\");\\n\\n const doubledNumber = slowFunction(number);\\n\\n return (\\n <div>\\n <input\\n type=\\\"number\\\"\\n value={number}\\n onChange={(e) => setNumber(e.target.value)}\\n />\\n <button onClick={() => setColor(color === \\\"black\\\" ? \\\"green\\\" : \\\"black\\\")}>\\n Change Color!\\n </button>\\n <p style={{ color: color }}>{doubledNumber}</p>\\n </div>\\n );\\n};\\n\\nexport default Demo;\\n
\\n\\n\\n\\nЧто произошло после нажатия кнопки изменения цвета?
\\n\\n\\n\\nimport { useState, useMemo } from \\\"react\\\";\\n\\nconst slowFunction = (num) => {\\n console.log(\\\"running slow double calculation...\\\");\\n for (let i = 0; i < 1000000000; i++) {}\\n return num * 2;\\n};\\n\\nconst Demo = () => {\\n const [number, setNumber] = useState(0);\\n const [color, setColor] = useState(\\\"black\\\");\\n\\n const doubledNumber = useMemo(() => {\\n return slowFunction(number);\\n }, [number]);\\n\\n return (\\n <div>\\n <input\\n type=\\\"number\\\"\\n value={number}\\n onChange={(e) => setNumber(e.target.value)}\\n />\\n <button onClick={() => setColor(color === \\\"black\\\" ? \\\"green\\\" : \\\"black\\\")}>\\n Change Color!\\n </button>\\n <p style={{ color: color }}>{doubledNumber}</p>\\n </div>\\n );\\n};\\n\\nexport default Demo;\\n
\\n\\n\\n\\nБлагодаря UseMemo нам больше не придется долго ждать установки цвета после изменения номера.
\\n\\n\\n\\nПричины следующие:
\\n\\n\\n\\nuseMemo. React. (n.d.). Получено 25 апреля 2023 года с https://react.dev/reference/react/useMemo.
\\n\\n\\n\\r\\nЧто такое useMemo? useMemo – это хук React, который позволяет кэшировать результат вычислений между рендерами. Как правило, useMemo уменьшает объем работы, которую необходимо выполнить при данном рендере. useMemo может мемоизировать функцию и ее результат, что означает, что если входы функции не изменяются, React вернет мемоизированное значение вместо повторного вычисления, что потенциально ускорит процесс рендеринга. Простое […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/react-usememo/\"],\"title\":[0,\"React UseMemo - Gorlov.\"],\"breadcrumbTitle\":[0,\"React UseMemo\"],\"description\":[0,\"useMemo - это хук React, который позволяет кэшировать результат вычислений между рендерами.\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"useMemo - это хук React, который позволяет кэшировать результат вычислений между рендерами.\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"React UseMemo - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:46:47+00:00\"],\"url\":[0,\"http://igorlov.loc/react-usememo/\"]}]}],\"title\":[0,\"React UseMemo\"],\"uri\":[0,\"/react-usememo/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"react-usememo\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"React\\\"],\\\"id\\\":[0,\\\"dGVybTozNg==\\\"],\\\"uri\\\":[0,\\\"/tag/react/\\\"]}],[0,{\\\"name\\\":[0,\\\"UseMemo\\\"],\\\"id\\\":[0,\\\"dGVybToyMTk=\\\"],\\\"uri\\\":[0,\\\"/tag/usememo/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExNTgw\"],\"node\":[0,{\"date\":[0,\"2023-04-27T20:40:10\"],\"content\":[0,\"\\nЕсли вы являетесь веб-разработчиком, вы будете работать с JavaScript при создании динамических и интерактивных веб-приложений. Одна из распространенных задач, которую вам придется выполнять, – это получение текущего URL веб-страницы.
\\n\\n\\n\\nВ этой статье вы узнаете, как получить текущий URL с помощью объекта Location в JavaScript. Я покажу вам несколько примеров, а также несколько лучших практик.
\\n\\n\\n\\nОбъект Location – это встроенный объект JavaScript, который предоставляет информацию о текущем URL веб-страницы. Он содержит различные свойства, позволяющие получать доступ и изменять различные части URL.
\\n\\n\\n\\nДля доступа к объекту Location можно использовать свойство window.location. Оно возвращает объект Location для текущей веб-страницы. Этот объект содержит множество данных, таких как URL, имя пути, происхождение, хост, данные поиска и другие.
\\n\\n\\n\\nНапример:
\\n\\n\\n\\n{\\n \\\"ancestorOrigins\\\": {\\n \\\"0\\\": \\\"https://codepen.io\\\"\\n },\\n \\\"href\\\": \\\"https://cdpn.io/cpe/boomboom/index.html?editors=0012&key=index.html-f1981af8-7dc2-f8b6-669a-8980d4a8d02a\\\",\\n \\\"origin\\\": \\\"https://cdpn.io\\\",\\n \\\"protocol\\\": \\\"https:\\\",\\n \\\"host\\\": \\\"cdpn.io\\\",\\n \\\"hostname\\\": \\\"cdpn.io\\\",\\n \\\"port\\\": \\\"\\\",\\n \\\"pathname\\\": \\\"/cpe/boomboom/index.html\\\",\\n \\\"search\\\": \\\"?editors=0012&key=index.html-f1981af8-7dc2-f8b6-669a-8980d4a8d02a\\\",\\n \\\"hash\\\": \\\"\\\"\\n}\\n
\\n\\n\\n\\nОдним из распространенных вариантов использования объекта Location является получение текущего URL веб-страницы. Это можно сделать, обратившись к свойству href объекта Location.
\\n\\n\\n\\nСвойство href содержит полный URL текущей веб-страницы:
\\n\\n\\n\\nconst currentUrl = window.location.href;\\nconsole.log(currentUrl);\\n
\\n\\n\\n\\nЭто приведет к записи текущего URL веб-страницы в консоль.
\\n\\n\\n\\nПомимо получения текущего URL, вам может понадобиться разобрать его, чтобы извлечь определенные части. Например, вы можете захотеть извлечь из URL протокол, хост или путь.
\\n\\n\\n\\nЧтобы разобрать текущий URL, вы можете использовать различные свойства объекта Location. Например, вы можете использовать свойство protocol, чтобы получить протокол текущего URL:
\\n\\n\\n\\nconst protocol = window.location.protocol;\\nconsole.log(protocol);\\n
\\n\\n\\n\\nВ результате в консоль будет выведен протокол текущего URL (например, “http:” или “https:”).
\\n\\n\\n\\nДругие свойства объекта Location, которые можно использовать для извлечения частей текущего URL, включают хост, имя хоста, порт, имя пути, поиск и хэш.
\\n\\n\\n\\nconst host = window.location.host;\\nconst pathname = window.location.pathname;\\nconst search = window.location.search;\\nconst hash = window.location.hash;\\n
\\n\\n\\n\\nИспользуя эти свойства, вы можете извлекать различные части текущего URL.
\\n\\n\\n\\nПомимо получения и разбора текущего URL, вам может потребоваться его обновление. Например, вам может понадобиться перенаправить пользователя на другой URL или динамически изменить текущий URL.
\\n\\n\\n\\nДля обновления текущего URL можно использовать различные методы объекта Location. Например, вы можете использовать метод replace() для замены текущего URL на новый URL:
\\n\\n\\n\\nconst newUrl = \\\"https://example.com/new-page.html\\\";\\nwindow.location.replace(newUrl);\\n
\\n\\n\\n\\nЭто заменит текущий URL на новый, перенаправляя пользователя на новую страницу.
\\n\\n\\n\\nПри работе с объектом Location есть несколько лучших практик, которым следует следовать, чтобы избежать возможных ошибок. Например, перед использованием объекта Location следует всегда проверять, доступен ли он.
\\n\\n\\n\\nif (window.location) {\\n // Access or modify the Location object\\n}\\n
\\n\\n\\n\\nВы также должны быть осторожны при изменении текущего URL-адреса, поскольку это может повлиять на работу пользователя в браузере. Например, не следует изменять протокол, хост или порт URL, если это не является абсолютно необходимым.
\\n\\n\\n\\nВ этой статье вы узнали, как получить текущий URL веб-страницы с помощью объекта Location в JavaScript. Поняв, как работать с объектом Location, вы сможете создавать более динамичные и интерактивные веб-приложения, обеспечивающие лучший пользовательский опыт.
\\n\\n\\n\\nСпасибо за прочтение, и я надеюсь, что вы нашли эту статью информативной и полезной. Для получения дополнительной информации о работе с URL-адресами в JavaScript вы можете прочитать статью о том, как обновить страницу с помощью JavaScript.
\\n\\n\\n\\nЕсли вы хотите узнать больше о JavaScript и веб-разработке, просмотрите 200+ экспертных статей по веб-разработке, написанных мной, а также загляните в мой блог, где вы найдете еще больше увлекательных материалов.
\\n\\n\\n\\nУспехов в кодировании!
\\n\\n\\n\\r\\nЕсли вы являетесь веб-разработчиком, вы будете работать с JavaScript при создании динамических и интерактивных веб-приложений. Одна из распространенных задач, которую вам придется выполнять, – это получение текущего URL веб-страницы. В этой статье вы узнаете, как получить текущий URL с помощью объекта Location в JavaScript. Я покажу вам несколько примеров, а также несколько лучших практик. Как […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/kak-poluchit-tekushhij-url-s-pomoshhyu-javascript-js-location/\"],\"title\":[0,\"Как получить текущий URL с помощью JavaScript -JS Location - Gorlov.\"],\"breadcrumbTitle\":[0,\"Как получить текущий URL с помощью JavaScript -JS Location\"],\"description\":[0,\"Если вы являетесь веб-разработчиком, вы будете работать с JavaScript при создании динамических и интерактивных веб-приложений. Одна из распространенных задач,\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Если вы являетесь веб-разработчиком, вы будете работать с JavaScript при создании динамических и интерактивных веб-приложений. Одна из распространенных задач,\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Как получить текущий URL с помощью JavaScript -JS Location - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:46:58+00:00\"],\"url\":[0,\"http://igorlov.loc/kak-poluchit-tekushhij-url-s-pomoshhyu-javascript-js-location/\"]}]}],\"title\":[0,\"Как получить текущий URL с помощью JavaScript -JS Location\"],\"uri\":[0,\"/kak-poluchit-tekushhij-url-s-pomoshhyu-javascript-js-location/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"kak-poluchit-tekushhij-url-s-pomoshhyu-javascript-js-location\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"JavaScript\\\"],\\\"id\\\":[0,\\\"dGVybTo0Mw==\\\"],\\\"uri\\\":[0,\\\"/tag/javascript/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMjc5\"],\"node\":[0,{\"date\":[0,\"2023-04-25T07:45:19\"],\"content\":[0,\"\\nВ Gutenberg 15.6 появился новый блок Details, расположенный в меню Experiments. После включения его можно использовать для переключения видимости скрытого содержимого. Это может быть полезно для представления таких вещей, как текстовые транскрипты для видео блоков или простые предупреждения о спойлерах. По умолчанию переключатель открыт в редакторе блоков, но закрыт на передней панели. Это первая итерация, поэтому поведение тумблера еще не доработано.
\\n\\n\\n\\nЕще один интересный эксперимент, который был реализован в версии 15.6, — это командный центр для редактора сайтов. Он был создан как быстрый поиск для перехода к другим страницам или шаблонам в редакторе.
\\n\\n\\n\\n“Это можно рассматривать как начало расширяемого компонента команд и быстрого поиска, который можно использовать для поиска путей (перейти на страницу “О сайте”; редактировать шаблон “Архив”) и запуска команд (переключить верхнюю панель инструментов и т.д.)”, — сказал ведущий архитектор Gutenberg Матиас Вентура в тикете, описывающем эту функцию. “Первым этапом является обеспечение быстрого поиска контента и шаблонов в редакторе сайта”.
\\n\\n\\n\\nИнженер Gutenberg Риад Бенгуэлла сказал, что его цель в рамках первоначального PR – создать API и компоненты для командного центра, но не обязательно реализовывать все команды. В конечном итоге он стремится к тому, чтобы эта функция делала следующее:
\\n\\n\\n\\nРазработчики Gutenberg все еще думают над тем, как лучше организовать и отобразить команды в зависимости от контекста, что является одной из причин, почему эта функция остается под флагом “Эксперименты”.
\\n\\n\\n\\nВ версии 15.6 в настройках блока Spacer также появились предустановки интервалов, что позволяет авторам тем создавать разумные предустановки, а пользователям – настраивать интервалы таким образом, чтобы они хорошо смотрелись на всех устройствах.
\\n\\n\\n\\n“После введения предустановок интервалов стало ясно из отзывов сообщества, что предустановки интервалов должны быть доступны и для регулятора Height в блоке Spacer”, — сказала в сообщении о выпуске сторонник разработчиков WordPress Биргит Паули-Хаак. “Это усовершенствование в версии 15.6 дает разработчикам тем гораздо больше гибкости в применении интервалов по всему сайту и обеспечивает плавность блоков Spacer”.
\\n\\n\\n\\nРазработчик ядра WordPress Ник Диего снял короткое видео о предустановках расстояния между блоками Spacer, в котором демонстрируется, как плавное изменение расстояния между блоками помогает содержимому и дизайну сайта лучше адаптироваться к различным видовым экранам.
\\n\\n\\n\\nGutenberg 15.6 вышел вчера, и одной из моих любимых новых функций стали предустановки интервалов для блока Spacer.
\\n\\n\\n\\nЕще несколько важных моментов в Gutenberg 15.6 включают следующее:
\\n\\n\\n\\nОзнакомьтесь с журналом изменений, чтобы увидеть все улучшения, исправления ошибок, а также обновления производительности и инструментария.
\\n\\n\\n\\n\\n\\n\\n\\r\\nВ Gutenberg 15.6 появился новый блок Details, расположенный в меню Experiments. После включения его можно использовать для переключения видимости скрытого содержимого. Это может быть полезно для представления таких вещей, как текстовые транскрипты для видео блоков или простые предупреждения о спойлерах. По умолчанию переключатель открыт в редакторе блоков, но закрыт на передней панели. Это первая итерация, […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Новости\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/novosti/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/gutenberg-15-6-predstavlyaet-eksperimentalnyj-blok-podrobnostej-i-komandnyj-czentr-dlya-redaktora-sajta/\"],\"title\":[0,\"Gutenberg 15.6 представляет экспериментальный блок подробностей и командный центр для редактора сайта - Gorlov.\"],\"breadcrumbTitle\":[0,\"Gutenberg 15.6 представляет экспериментальный блок подробностей и командный центр для редактора сайта\"],\"description\":[0,\"В Gutenberg 15.6 появился новый блок Details, расположенный в меню Experiments. После включения его можно использовать для переключения видимости скрытого\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"В Gutenberg 15.6 появился новый блок Details, расположенный в меню Experiments. После включения его можно использовать для переключения видимости скрытого\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Gutenberg 15.6 представляет экспериментальный блок подробностей и командный центр для редактора сайта - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:47:08+00:00\"],\"url\":[0,\"http://igorlov.loc/gutenberg-15-6-predstavlyaet-eksperimentalnyj-blok-podrobnostej-i-komandnyj-czentr-dlya-redaktora-sajta/\"]}]}],\"title\":[0,\"Gutenberg 15.6 представляет экспериментальный блок подробностей и командный центр для редактора сайта\"],\"uri\":[0,\"/gutenberg-15-6-predstavlyaet-eksperimentalnyj-blok-podrobnostej-i-komandnyj-czentr-dlya-redaktora-sajta/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"gutenberg-15-6-predstavlyaet-eksperimentalnyj-blok-podrobnostej-i-komandnyj-czentr-dlya-redaktora-sajta\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Новости\\\"],\\\"uri\\\":[0,\\\"/category/novosti/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Gutenberg\\\"],\\\"id\\\":[0,\\\"dGVybToyMTg=\\\"],\\\"uri\\\":[0,\\\"/tag/gutenberg/\\\"]}],[0,{\\\"name\\\":[0,\\\"WordPress\\\"],\\\"id\\\":[0,\\\"dGVybToyOA==\\\"],\\\"uri\\\":[0,\\\"/tag/wordpress/\\\"]}],[0,{\\\"name\\\":[0,\\\"Новости\\\"],\\\"id\\\":[0,\\\"dGVybTox\\\"],\\\"uri\\\":[0,\\\"/category/novosti/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMjg1\"],\"node\":[0,{\"date\":[0,\"2023-04-25T07:41:58\"],\"content\":[0,\"\\nСуществует три способа использования компонента в приложении Angular.
\\n\\n\\n\\nОднако эта статья посвящена динамической и ленивой загрузке компонента. Основным преимуществом ленивой загрузки компонента является уменьшение размера начального пакета и загрузка компонента в браузер только при необходимости.
\\n\\n\\n\\nДопустим, у вас есть компонент под названием GreetComponent, как показано в следующем блоке кода,
\\n\\n\\n\\nimport { Component, EventEmitter, Input, Output } from '@angular/core';\\nimport { CommonModule } from '@angular/common';\\n \\nconst template = `\\n <h2>{{message}} </h2>\\n <button (click)='sendMessage()'>Send Message </button>\\n`\\n@Component({\\n selector: 'app-greet',\\n standalone: true,\\n imports: [CommonModule],\\n template: template\\n})\\nexport class GreetComponent {\\n @Input({ required: true }) message?: string;\\n @Output() messageEvent = new EventEmitter<boolean>();\\n sendMessage(): void {\\n this.messageEvent.emit(true);\\n }\\n} \\n
\\n\\n\\n\\nКомпонент GreetComponent имеет украшенное свойство @Input и украшенный @Output() EvenEmmiter. Компонент FooComponent использует его в качестве дочернего компонента, как показано в следующем блоке кода.
\\n\\n\\n\\nconst template = `\\n <app-greet [message]='message' (messageEvent)='sendMessage($event)'></app-greet>\\n`\\n@Component({\\n selector: 'app-foo',\\n standalone: true,\\n imports: [CommonModule,\\n GreetComponent],\\n template : template\\n})\\nexport class FooComponent {\\n message = \\\"data from parent\\\"\\n \\n sendMessage(m:boolean){\\n console.log(m);\\n }\\n}\\n
\\n\\n\\n\\nЗдесь стоит обратить внимание на несколько моментов,
\\n\\n\\n\\nИз-за этих двух моментов, каждый раз, когда Angular компилирует FooComponent, он также включает GreetComponent, увеличивая размер пакета, содержащего FooComponent.
\\n\\n\\n\\n*Одним из основных преимуществ динамической (ленивой) загрузки компонента является уменьшение размера пакета, поскольку он загружается в браузер только тогда, когда это необходимо. *
\\n\\n\\n\\nДопустим, компонент GreetComponent должен загружаться динамически и лениво при нажатии кнопки в FooComponent. Для этого,
\\n\\n\\n\\nconst template = `\\n <button (click)='loadComponent()'>Load Greet Component </button>\\n`\\n@Component({\\n selector: 'app-foo',\\n standalone: true,\\n imports: [CommonModule],\\n template: template\\n})\\nexport class FooComponent {\\n message = \\\"data from parent\\\"\\n greetcomp: any;\\n
\\n\\n\\n\\nЧтобы динамически загрузить компонент, инжектируйте ViewContainerRef с помощью функции inject или инжекта конструктора.
\\n\\n\\n\\nvcr = inject(ViewContainerRef);
\\n\\n\\n\\nПосле этого импортируйте файл, содержащий GreetComponent, с помощью оператора import. Оператор import используется в JavaScript для динамической загрузки файла.
\\n\\n\\n\\nconst { GreetComponent } = await import(‘../greet/greet.component’);
\\n\\n\\n\\nПосле импорта файла используйте метод CreateComponent в ViewContainerRef.
\\n\\n\\n\\nthis.greetcomp = this.vcr.createComponent(GreetComponent);
\\n\\n\\n\\nВы можете получить доступ ко всем свойствам и событиям динамически загруженных компонентов, используя метод экземпляра. Так, свойству message можно передать значение a, как показано в следующем блоке кода,
\\n\\n\\n\\nthis.greetcomp.instance.message = “Hello dynamic Component”;
\\n\\n\\n\\nВы можете подписаться на EventEmitter, украшенный @Output, как показано далее,
\\n\\n\\n\\nthis.greetcomp.instance.messageEvent.subscribe((data:any)=>{\\n console.log(data);\\n })\\n\\n
\\n\\n\\n\\nСобрав все вместе, вы можете лениво загрузить GreetComponent нажатием кнопки в FooComponent, как показано в следующем листинге кода,
\\n\\n\\n\\nconst template = `\\n <button (click)='loadComponent()'>Load Greet Component </button>\\n`\\n@Component({\\n selector: 'app-foo',\\n standalone: true,\\n imports: [CommonModule],\\n template: template\\n})\\nexport class FooComponent {\\n message = \\\"data from parent\\\"\\n greetcomp: any;\\n vcr = inject(ViewContainerRef);\\n \\n async loadComponent() {\\n this.vcr.clear();\\n const { GreetComponent } = await import('../greet/greet.component');\\n this.greetcomp = this.vcr.createComponent(GreetComponent);\\n if (this.greetcomp) {\\n this.greetcomp.instance.message = \\\"Hello dynamic Component\\\";\\n \\n this.greetcomp.instance.messageEvent.subscribe((data:any)=>{\\n console.log(data);\\n })\\n }\\n \\n }\\n}\\n\\n
\\n\\n\\n\\nВ настоящее время Angular загружает GreetComponent в конце после всех DOM-элементов FooComponent. Чтобы загрузить его в определенный div-блок, считайте div-блок как ViewChild и ViewConatinerRef. В другом посте я расскажу об этом подробнее.
\\n\\n\\n\\nНадеюсь, эта статья будет вам полезна. Спасибо за прочтение.
\\n\\n\\n\\r\\nСуществует три способа использования компонента в приложении Angular. Однако эта статья посвящена динамической и ленивой загрузке компонента. Основным преимуществом ленивой загрузки компонента является уменьшение размера начального пакета и загрузка компонента в браузер только при необходимости. Допустим, у вас есть компонент под названием GreetComponent, как показано в следующем блоке кода, Компонент GreetComponent имеет украшенное свойство @Input […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Как закодить\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/kak-zakodit/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/kak-lenivo-i-dinamicheski-zagruzit-komponent-v-angular/\"],\"title\":[0,\"Как лениво и динамически загрузить компонент в Angular - Gorlov.\"],\"breadcrumbTitle\":[0,\"Как лениво и динамически загрузить компонент в Angular\"],\"description\":[0,\"Существует три способа использования компонента в приложении Angular.\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Существует три способа использования компонента в приложении Angular.\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Как лениво и динамически загрузить компонент в Angular - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:47:18+00:00\"],\"url\":[0,\"http://igorlov.loc/kak-lenivo-i-dinamicheski-zagruzit-komponent-v-angular/\"]}]}],\"title\":[0,\"Как лениво и динамически загрузить компонент в Angular\"],\"uri\":[0,\"/kak-lenivo-i-dinamicheski-zagruzit-komponent-v-angular/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"kak-lenivo-i-dinamicheski-zagruzit-komponent-v-angular\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Angular\\\"],\\\"id\\\":[0,\\\"dGVybToxNDQ=\\\"],\\\"uri\\\":[0,\\\"/tag/angular/\\\"]}],[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"id\\\":[0,\\\"dGVybToyNg==\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMjg4\"],\"node\":[0,{\"date\":[0,\"2023-04-24T08:39:23\"],\"content\":[0,\"\\nX-crawl – это гибкая многофункциональная библиотека Node.js для краулеров. Используется для переползания страниц, переползания интерфейсов, переползания файлов и опроса переползающих страниц.
\\n\\n\\n\\nЕсли вам также нравится x-crawl, вы можете дать репозиторию x-crawl звезду, чтобы поддержать его, спасибо за вашу поддержку!
\\n\\n\\n\\n🔥 AsyncSync – Просто измените значение атрибута mode, чтобы переключить режим асинхронного или синхронного ползания.
\\n\\n\\n\\n⚙️Multiple функций — Он может ползать по страницам, ползать по интерфейсам, ползать по файлам и опрашивать ползание, а также поддерживает одиночное или множественное ползание.
\\n\\n\\n\\n🖋️ Гибкий стиль написания — Простая конфигурация цели, подробная конфигурация цели, смешанная конфигурация массива целей и расширенная конфигурация, один и тот же API ползания может адаптироваться к нескольким конфигурациям.
\\n\\n\\n\\n👀Device Fingerprinting – нулевая конфигурация или пользовательская конфигурация, чтобы избежать отпечатков пальцев для идентификации и отслеживания нас из разных мест.
\\n\\n\\n\\n⏱️ Interval Crawling – отсутствие интервала, фиксированный интервал и случайный интервал могут генерировать или избежать высокой одновременности ползания.
\\n\\n\\n\\n🔄 Повторная попытка при неудаче – глобальные настройки, локальные настройки и индивидуальные настройки, это позволяет избежать неудач при ползании, вызванных временными проблемами.
\\n\\n\\n\\n🚀 Очередь приоритетов – В соответствии с приоритетом одной цели ползания, она может быть пройдена раньше других целей.
\\n\\n\\n\\n☁️ Crawl SPA – Crawl SPA (Single Page Application) для создания предварительно отрендеренного контента (он же “SSR” (Server Side Rendering)).
\\n\\n\\n\\n⚒️ Controlling Pages – Безголовые браузеры могут отправлять формы, нажатия клавиш, действия по событиям, генерировать скриншоты страниц и т.д.
\\n\\n\\n\\n🧾 Захват записей – Захват и запись результатов ползания и другой информации, а также выделение напоминаний на консоли.
\\n\\n\\n\\n🦾 TypeScript – Собственные типы, реализация полных типов через дженерики.
\\n\\n\\n\\nВ качестве примера возьмем несколько фотографий из опыта Airbnb hawaii и Plus listings автоматически каждый день:
\\n\\n\\n\\n// 1.Import module ES/CJS\\nimport xCrawl from 'x-crawl'\\n\\n// 2.Create a crawler instance\\nconst myXCrawl = xCrawl({ maxRetry: 3, intervalTime: { max: 3000, min: 2000 } })\\n\\n// 3.Set the crawling task\\n/*\\n Call the startPolling API to start the polling function,\\n and the callback function will be called every other day\\n*/\\nmyXCrawl.startPolling({ d: 1 }, async (count, stopPolling) => {\\n // Call crawlPage API to crawl Page\\n const res = await myXCrawl.crawlPage([\\n 'https://zh.airbnb.com/s/hawaii/experiences',\\n 'https://zh.airbnb.com/s/hawaii/plus_homes'\\n ])\\n\\n // Store the image URL to targets\\n const targets = []\\n const elSelectorMap = ['.c14whb16', '.a1stauiv']\\n for (const item of res) {\\n const { id } = item\\n const { page } = item.data\\n\\n // Gets the URL of the page's wheel image element\\n const boxHandle = await page.$(elSelectorMap[id - 1])\\n const urls = await boxHandle!.$eval('picture img', (imgEls) => {\\n return imgEls.map((item) => item.src)\\n })\\n targets.push(...urls)\\n\\n // Close page\\n page.close()\\n }\\n\\n // Call the crawlFile API to crawl pictures\\n myXCrawl.crawlFile({ targets, storeDir: './upload' })\\n})\\n
\\n\\n\\n\\nПримечание: Не ползайте по своему усмотрению, вы можете проверить протокол robots.txt перед ползанием. Это просто демонстрация того, как использовать x-crawl.
\\n\\n\\n\\r\\nX-crawl X-crawl – это гибкая многофункциональная библиотека Node.js для краулеров. Используется для переползания страниц, переползания интерфейсов, переползания файлов и опроса переползающих страниц. Если вам также нравится x-crawl, вы можете дать репозиторию x-crawl звезду, чтобы поддержать его, спасибо за вашу поддержку! Особенности 🔥 AsyncSync – Просто измените значение атрибута mode, чтобы переключить режим асинхронного или синхронного […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Как закодить\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/kak-zakodit/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/gibkaya-mnogofunkczionalnaya-biblioteka-node-js-dlya-kraulerov-x-crawl/\"],\"title\":[0,\"Гибкая многофункциональная библиотека Node.js для краулеров - X-crawl - Gorlov.\"],\"breadcrumbTitle\":[0,\"Гибкая многофункциональная библиотека Node.js для краулеров — x-crawl\"],\"description\":[0,\"X-crawl - это гибкая многофункциональная библиотека Node.js для краулеров. Используется для переползания страниц, переползания интерфейсов, переползания\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"X-crawl - это гибкая многофункциональная библиотека Node.js для краулеров. Используется для переползания страниц, переползания интерфейсов, переползания\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Гибкая многофункциональная библиотека Node.js для краулеров - X-crawl - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:47:26+00:00\"],\"url\":[0,\"http://igorlov.loc/gibkaya-mnogofunkczionalnaya-biblioteka-node-js-dlya-kraulerov-x-crawl/\"]}]}],\"title\":[0,\"Гибкая многофункциональная библиотека Node.js для краулеров — x-crawl\"],\"uri\":[0,\"/gibkaya-mnogofunkczionalnaya-biblioteka-node-js-dlya-kraulerov-x-crawl/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"gibkaya-mnogofunkczionalnaya-biblioteka-node-js-dlya-kraulerov-x-crawl\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Node.js\\\"],\\\"id\\\":[0,\\\"dGVybToyMDQ=\\\"],\\\"uri\\\":[0,\\\"/tag/node-js/\\\"]}],[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"id\\\":[0,\\\"dGVybToyNg==\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}],[0,{\\\"name\\\":[0,\\\"Парсер\\\"],\\\"id\\\":[0,\\\"dGVybToyMTc=\\\"],\\\"uri\\\":[0,\\\"/tag/parser/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMjkw\"],\"node\":[0,{\"date\":[0,\"2023-04-24T08:35:43\"],\"content\":[0,\"\\nJenkins – это широко используемый сервер автоматизации, который помогает оптимизировать процессы разработки программного обеспечения. Docker, с другой стороны, является популярным инструментом для создания и управления контейнерами. Запуск Jenkins в контейнере Docker дает ряд преимуществ, таких как простота развертывания и переносимость. Однако важно убедиться, что вы используете правильную версию Jenkins, чтобы обеспечить совместимость с существующими инструментами и процессами. В этом руководстве мы рассмотрим, как запустить докер-контейнер Jenkins с определенной версией, гарантируя, что у вас есть нужные инструменты для работы.
\\n\\n\\n\\nИспользуйте следующий реестр docker для извлечения образа docker для запуска Docker-образа Jenkins:
https://hub.docker.com/r/bitnami/jenkins
Ниже приведена команда docker pull для извлечения версии 2.346.3:
\\n\\n\\n\\ndocker pull bitnami/jenkins:2.346.3\\n
\\n\\n\\n\\nПосле завершения процесса pull мы можем запустить Docker-контейнер Jenkins с помощью следующей команды:
\\n\\n\\n\\ndocker volume create --name jenkins_data\\ndocker run -d -p 8080:8080 --name jenkins \\\\\\n --network jenkins-network \\\\\\n --volume jenkins_data:/bitnami/jenkins \\\\\\n bitnami/jenkins:2.346.3\\n
\\n\\n\\n\\nЗдесь я задал отображение порта с 8080 на 8080. Если вам нужен другой порт (например, 9000), используйте опцию -p 9000:8080.
\\n\\n\\n\\nПройдет некоторое время, и вы сможете увидеть журналы jenkins с помощью команды docker logs .
\\n\\n\\n\\nЗатем откройте браузер и перейдите по адресу http://localhost:8080 и вы увидите страницу входа в систему Jenkins. Используйте следующие учетные данные для входа в систему:
Имя пользователя: user
Пароль: bitnami
Jenkins – это широко используемый сервер автоматизации, который помогает оптимизировать процессы разработки программного обеспечения. Docker, с другой стороны, является популярным инструментом для создания и управления контейнерами. Запуск Jenkins в контейнере Docker дает ряд преимуществ, таких как простота развертывания и переносимость. Однако важно убедиться, что вы используете правильную версию Jenkins, чтобы обеспечить совместимость с существующими инструментами […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Как закодить\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/kak-zakodit/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/kak-zapustit-kontejner-jenkins-docker-s-zadannoj-versiej/\"],\"title\":[0,\"Как запустить контейнер Jenkins Docker с заданной версией - Gorlov.\"],\"breadcrumbTitle\":[0,\"Как запустить контейнер Jenkins Docker с заданной версией\"],\"description\":[0,\"Jenkins - это широко используемый сервер автоматизации, который помогает оптимизировать процессы разработки программного обеспечения. Docker, с другой\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Jenkins - это широко используемый сервер автоматизации, который помогает оптимизировать процессы разработки программного обеспечения. Docker, с другой\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Как запустить контейнер Jenkins Docker с заданной версией - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:47:34+00:00\"],\"url\":[0,\"http://igorlov.loc/kak-zapustit-kontejner-jenkins-docker-s-zadannoj-versiej/\"]}]}],\"title\":[0,\"Как запустить контейнер Jenkins Docker с заданной версией\"],\"uri\":[0,\"/kak-zapustit-kontejner-jenkins-docker-s-zadannoj-versiej/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"kak-zapustit-kontejner-jenkins-docker-s-zadannoj-versiej\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Ci/Cd\\\"],\\\"id\\\":[0,\\\"dGVybToyMTY=\\\"],\\\"uri\\\":[0,\\\"/tag/ci-cd/\\\"]}],[0,{\\\"name\\\":[0,\\\"Docker\\\"],\\\"id\\\":[0,\\\"dGVybToxNjc=\\\"],\\\"uri\\\":[0,\\\"/tag/docker/\\\"]}],[0,{\\\"name\\\":[0,\\\"Jenkins\\\"],\\\"id\\\":[0,\\\"dGVybToyMTU=\\\"],\\\"uri\\\":[0,\\\"/tag/jenkins/\\\"]}],[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"id\\\":[0,\\\"dGVybToyNg==\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMjky\"],\"node\":[0,{\"date\":[0,\"2023-04-24T08:33:43\"],\"content\":[0,\"\\nПопулярным и практичным примером использования НФТ является генерирование билетов на живые мероприятия. Блокчейн, такой как Ethereum, может гарантировать право собственности, создателя и подлинность цифрового товара, эффективно решая проблему поддельных билетов. Пока крупные игроки, такие как Ticketmaster, борются со скальперами (отчаянно пытающимися контролировать, кто, где и за сколько может перепродавать билеты) и мошенничеством с билетами, у Web3 уже есть решение. Индустрия продажи билетов созрела для разрушения.
\\n\\n\\n\\nВ этом руководстве мы рассмотрим, как создать такое решение для продажи билетов с помощью ConsenSys Truffle, Infura и Infura NFT API. Мы развернем смарт-контракт, который действует как служба продажи билетов и создает билеты в виде неиграбельных токенов ERC-20 (NFT). Мы также рассмотрим несколько архитектур потенциальных фронтендов, которые могут взаимодействовать с контрактом и вместе функционировать как интегрированная, полнофункциональная, web3 система продажи билетов.
\\n\\n\\n\\nДавайте приступим к созданию!
\\n\\n\\n\\nБазовая архитектура нашей системы предполагает создание смарт-контракта, который выпускает наши билеты в виде неиграбельных токенов (NFT). NFT идеально подходят для того, что мы хотим создать. Это доказательно уникальные цифровые токены, которые позволяют нам гарантировать, что каждый билет уникален и не может быть скопирован или подделан. Это не только гарантирует безопасность билетов для зрителей, но и дает артистам (и организаторам мероприятий) больше возможностей для контроля над распространением, ценообразованием и перепродажей билетов. Использование смарт-контрактов и НФТ даже позволяет создать новые источники дохода, такие как роялти и разделение доходов!
\\n\\n\\n\\n(Если вам нужна справочная информация о любом из этих терминов, технологии blockchain или web3 в целом, ознакомьтесь с этой статьей о том, как стать Web3-разработчиком, изучив стек Web3).
\\n\\n\\n\\nПервое, что мы собираемся сделать, это установить кошелек MetaMask и добавить в него тестовую сеть Sepolia. MetaMask – это самый популярный в мире, безопасный и простой в использовании цифровой кошелек для самостоятельного хранения средств.
\\n\\n\\n\\nСначала загрузите расширение MetaMask. После установки расширения MetaMask настроит для вас кошелек. В процессе вам будет предоставлена секретная фраза. Храните ее в безопасности и ни в коем случае не разглашайте.
\\n\\n\\n\\nПосле того как вы настроите MetaMask, нажмите на вкладку Сеть в правом верхнем углу. Вы увидите опцию показать/скрыть тестовые сети.
\\n\\n\\n\\nКогда вы включите опцию тестовых сетей, вы сможете увидеть тестовую сеть Sepolia в выпадающем меню. Мы хотим использовать сеть Sepolia, чтобы мы могли развернуть и протестировать нашу систему, не тратя реальных денег.
\\n\\n\\n\\nДля того чтобы развернуть наш смарт-контракт и взаимодействовать с ним, нам потребуется немного бесплатных тестовых ETH. Вы можете получить бесплатные ETH Sepolia из крана Sepolia.
\\n\\n\\n\\nКак только вы пополните свой кошелек, вы должны увидеть ненулевой баланс при переключении на тестовую сеть Sepolia на MetaMask.
\\n\\n\\n\\nКак и все Ethereum dapps, мы будем собирать наш проект с помощью node и npm. Если они не установлены на вашей локальной машине, вы можете сделать это здесь.
\\n\\n\\n\\nЧтобы убедиться, что все работает правильно, выполните следующую команду:
\\n\\n\\n\\n$ node -v\\n
\\n\\n\\n\\nЕсли все прошло успешно, вы должны увидеть номер версии Node.
\\n\\n\\n\\nДля того чтобы развернуть наш контракт в сети Sepolia, нам понадобится учетная запись Infura. Infura предоставляет нам доступ к конечным точкам RPC, которые обеспечивают быстрый, надежный и простой доступ к выбранному нами блокчейну.
\\n\\n\\n\\nЗарегистрируйтесь для получения бесплатной учетной записи. После создания учетной записи перейдите на приборную панель и выберите “Создать новый ключ”.
\\n\\n\\n\\nДля сети выберите Web3 API и назовите ее Ticketing System или как-нибудь по своему усмотрению.
\\n\\n\\n\\nПосле того как вы нажмете кнопку Create, Infura сгенерирует для вас ключ API и автоматически предоставит вам конечные точки RPC для Ethereum, Goerli, Sepolia, L2 и не-EVM L1 (и соответствующих им тестовых сетей).
\\n\\n\\n\\nВ данном руководстве нас интересует только конечная точка RPC Sepolia. Этот URL имеет вид https://sepolia.infura.io/v3/←API KEY→.
\\n\\n\\n\\nДавайте создадим пустой репозиторий проекта, выполнив следующие команды:
\\n\\n\\n\\n$ mkdir nft-ticketing && cd nft-ticketing\\n$ npm init -y\\n
\\n\\n\\n\\nДля создания и развертывания нашего криптовалютного смарт-контракта мы будем использовать Truffle, среду разработки мирового класса и механизм тестирования смарт-контрактов EVM. Установите Truffle, выполнив:
\\n\\n\\n\\n$ npm install —save truffle\\n
\\n\\n\\n\\nТеперь мы можем создать пустой проект Truffle, выполнив следующую команду:
\\n\\n\\n\\n$ npx truffle init\\n
\\n\\n\\n\\nЧтобы проверить, все ли работает правильно, выполните
\\n\\n\\n\\n$ npx truffle test\\n
\\n\\n\\n\\nТеперь мы успешно настроили Truffle. Далее установим пакет контрактов OpenZeppelin. Этот пакет даст нам доступ к базовой реализации ERC-721 (стандарт для неиграбельных токенов), а также к нескольким полезным дополнительным функциям.
\\n\\n\\n\\n$ npm install @openzeppelin/contracts\\n
\\n\\n\\n\\nЧтобы позволить Truffle использовать наш кошелек MetaMask, подписывать транзакции и оплачивать газ от нашего имени, нам потребуется еще один пакет под названием hdwalletprovider. Установите его с помощью следующей команды:
\\n\\n\\n\\n$ npm install @truffle/hdwallet-provider\\n
\\n\\n\\n\\nНаконец, чтобы сохранить конфиденциальную информацию о нашем кошельке в безопасности, мы будем использовать пакет dotenv.
\\n\\n\\n\\n$ npm install dotenv\\n
\\n\\n\\n\\nОткройте репозиторий проекта в редакторе кода (например: VS Code). В папке contracts создайте новый файл под названием NftTicketing.sol.
\\n\\n\\n\\nНаш билетный контракт будет наследовать все функциональные возможности, предлагаемые ERC721Enumerable реализацией OpenZeppelin. Сюда входят переводы, отслеживание метаданных, данные о владельцах и т.д.
\\n\\n\\n\\nМы реализуем следующие функции с нуля:
\\n\\n\\n\\nДобавьте следующий код в файл NftTicketing.sol.
\\n\\n\\n\\n//SPDX-License-Identifier: MIT\\npragma solidity ^0.8.19;\\n\\nimport \\\"@openzeppelin/contracts/token/ERC721/ERC721.sol\\\";\\nimport \\\"@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol\\\";\\nimport \\\"@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol\\\";\\nimport \\\"@openzeppelin/contracts/access/Ownable.sol\\\";\\nimport \\\"@openzeppelin/contracts/utils/Counters.sol\\\";\\nimport \\\"@openzeppelin/contracts/utils/Base64.sol\\\";\\nimport \\\"@openzeppelin/contracts/utils/Strings.sol\\\";\\n\\ncontract NftTicketing is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable {\\n using Counters for Counters.Counter;\\n\\n Counters.Counter private _tokenIds;\\n\\n // Total number of tickets available for the event\\n uint public constant MAX_SUPPLY = 10000;\\n\\n // Number of tickets you can book at a time; prevents spamming\\n uint public constant MAX_PER_MINT = 5;\\n\\n string public baseTokenURI;\\n\\n // Price of a single ticket\\n uint public price = 0.05 ether;\\n\\n // Flag to turn sales on and off\\n bool public saleIsActive = false;\\n\\n // Give collection a name and a ticker\\n constructor() ERC721(\\\"My NFT Tickets\\\", \\\"MNT\\\") {}\\n\\n // Generate NFT metadata\\n function generateMetadata(uint tokenId) public pure returns (string memory) {\\n string memory svg = string(abi.encodePacked(\\n \\\"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" preserveAspectRatio='xMinyMin meet' viewBox='0 0 350 350'>\\\",\\n \\\"<style>.base { fill: white; font-family: serif; font-size: 25px; }</style>\\\",\\n \\\"<rect width=\\\"100%\\\" height=\\\"100%\\\" fill=\\\"red\\\" />\\\",\\n \\\"<text x='50%' y='40%' class=\\\"base\\\" dominant-baseline=\\\"middle\\\" text-anchor=\\\"middle\\\">\\\",\\n \\\"<tspan y='50%' x='50%'>NFT Ticket #\\\",\\n Strings.toString(tokenId),\\n \\\"</tspan></text></svg>\\\"\\n ));\\n\\n string memory json = Base64.encode(\\n bytes(\\n string(\\n abi.encodePacked(\\n '{\\\"name\\\": \\\"NFT Ticket #',\\n Strings.toString(tokenId),\\n '\\\", \\\"description\\\": \\\"A ticket that gives you access to a cool event!\\\", \\\"image\\\": \\\"data:image/svg+xml;base64,',\\n Base64.encode(bytes(svg)),\\n '\\\", \\\"attributes\\\": [{\\\"trait_type\\\": \\\"Type\\\", \\\"value\\\": \\\"Base Ticket\\\"}]}'\\n )\\n )\\n )\\n );\\n\\n string memory metadata = string(\\n abi.encodePacked(\\\"data:application/json;base64,\\\", json)\\n );\\n return metadata;\\n }\\n\\n // Reserve tickets to creator wallet\\n function reserveNfts(uint _count) public onlyOwner {\\n uint nextId = _tokenIds.current();\\n\\n require(nextId + _count < MAX_SUPPLY, \\\"Not enough NFTs left to reserve\\\");\\n\\n for (uint i = 0; i < _count; i++) {\\n string memory metadata = generateMetadata(nextId + i);\\n _mintSingleNft(msg.sender, metadata);\\n }\\n }\\n\\n // Airdrop NFTs\\n function airDropNfts(address[] calldata _wAddresses) public onlyOwner {\\n uint nextId = _tokenIds.current();\\n uint count = _wAddresses.length;\\n\\n require(nextId + count < MAX_SUPPLY, \\\"Not enough NFTs left to reserve\\\");\\n\\n for (uint i = 0; i < count; i++) {\\n string memory metadata = generateMetadata(nextId + i);\\n _mintSingleNft(_wAddresses[i], metadata);\\n }\\n }\\n\\n // Set Sale state\\n function setSaleState(bool _activeState) public onlyOwner {\\n saleIsActive = _activeState;\\n }\\n\\n // Allow public to mint NFTs\\n function mintNfts(uint _count) public payable {\\n\\n uint nextId = _tokenIds.current();\\n\\n require(nextId + _count < MAX_SUPPLY, \\\"Not enough NFT tickets left!\\\");\\n require(_count > 0 && _count <= MAX_PER_MINT, \\\"Cannot mint specified number of NFT tickets.\\\");\\n require(saleIsActive, \\\"Sale is not currently active!\\\");\\n require(msg.value >= price * _count, \\\"Not enough ether to purchase NFTs.\\\");\\n\\n for (uint i = 0; i < _count; i++) {\\n string memory metadata = generateMetadata(nextId + i);\\n _mintSingleNft(msg.sender, metadata);\\n }\\n }\\n\\n // Mint a single NFT ticket\\n function _mintSingleNft(address _wAddress, string memory _tokenURI) private {\\n // Sanity check for absolute worst case scenario\\n require(totalSupply() == _tokenIds.current(), \\\"Indexing has broken down!\\\");\\n uint newTokenID = _tokenIds.current();\\n _safeMint(_wAddress, newTokenID);\\n _setTokenURI(newTokenID, _tokenURI);\\n _tokenIds.increment();\\n }\\n\\n // Update price\\n function updatePrice(uint _newPrice) public onlyOwner {\\n price = _newPrice;\\n }\\n\\n // Withdraw ether\\n function withdraw() public payable onlyOwner {\\n uint balance = address(this).balance;\\n require(balance > 0, \\\"No ether left to withdraw\\\");\\n\\n (bool success, ) = (msg.sender).call{value: balance}(\\\"\\\");\\n require(success, \\\"Transfer failed.\\\");\\n }\\n\\n // Get tokens of an owner\\n function tokensOfOwner(address _owner) external view returns (uint[] memory) {\\n\\n uint tokenCount = balanceOf(_owner);\\n uint[] memory tokensId = new uint256[](tokenCount);\\n\\n for (uint i = 0; i < tokenCount; i++) {\\n tokensId[i] = tokenOfOwnerByIndex(_owner, i);\\n }\\n return tokensId;\\n }\\n\\n // The following functions are overrides required by Solidity.\\n function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)\\n internal\\n override(ERC721, ERC721Enumerable)\\n {\\n super._beforeTokenTransfer(from, to, tokenId, batchSize);\\n }\\n\\n function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {\\n super._burn(tokenId);\\n }\\n\\n function tokenURI(uint256 tokenId)\\n public\\n view\\n override(ERC721, ERC721URIStorage)\\n returns (string memory)\\n {\\n return super.tokenURI(tokenId);\\n }\\n\\n function supportsInterface(bytes4 interfaceId)\\n public\\n view\\n override(ERC721, ERC721Enumerable)\\n returns (bool)\\n {\\n return super.supportsInterface(interfaceId);\\n }\\n}\\n
\\n\\n\\n\\nУбедитесь, что контракт компилируется правильно, выполнив команду:
\\n\\n\\n\\nnpx truffle compile\\n
\\n\\n\\n\\nНаш контракт уже довольно сложный, но можно добавить некоторые дополнительные функции по вашему усмотрению.
\\n\\n\\n\\nНапример, вы можете реализовать механизм защиты от скальпирования в вашем контракте. Это можно сделать следующим образом:
\\n\\n\\n\\nДобавьте следующий фрагмент ниже конструктора контракта:
\\n\\n\\n\\nmapping(address => bool) canMintMultiple;\\n\\n // Function that allowlists addresses to hold multiple NFTs.\\n function addToAllowlist(address[] calldata _wAddresses) public onlyOwner {\\n for (uint i = 0; i < _wAddresses.length; i++) {\\n canMintMultiple[_wAddresses[i]] = true;\\n }\\n }\\n
\\n\\n\\n\\nНаконец, измените функцию _beforeTokenTranfer следующим образом:
\\n\\n\\n\\n// The following functions are overrides required by Solidity.\\n function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)\\n internal\\n override(ERC721, ERC721Enumerable)\\n {\\n if (balanceOf(to) > 0) {\\n require(to == owner() || canMintMultiple[to], \\\"Not authorized to hold more than one ticket\\\");\\n }\\n super._beforeTokenTransfer(from, to, tokenId, batchSize);\\n }\\n
\\n\\n\\n\\nСкомпилируйте контракт еще раз, используя приведенную выше команду Truffle.
\\n\\n\\n\\nСоздайте новый файл в корневом каталоге проекта под названием .env и добавьте в него следующее содержимое:
\\n\\n\\n\\nINFURA_API_KEY = \\\"https://sepolia.infura.io/v3/<Your-API-Key>\\\"\\nMNEMONIC = \\\"<Your-MetaMask-Secret-Recovery-Phrase>\\\"\\n
\\n\\n\\n\\nДалее добавим информацию о нашем кошельке, конечной точке RPC Infura и сети Sepolia в файл конфигурации Truffle. Замените содержимое файла truffle.config.js на следующее:
\\n\\n\\n\\nrequire('dotenv').config();\\nconst HDWalletProvider = require('@truffle/hdwallet-provider');\\nconst { INFURA_API_KEY, MNEMONIC } = process.env;\\n\\nmodule.exports = {\\n networks: {\\n development: {\\n host: \\\"127.0.0.1\\\",\\n port: 8545,\\n network_id: \\\"*\\\"\\n },\\n sepolia: {\\n provider: () => new HDWalletProvider(MNEMONIC, INFURA_API_KEY),\\n network_id: '5',\\n }\\n }\\n};\\n
\\n\\n\\n\\nТеперь давайте напишем скрипт для развертывания нашего контракта на блокчейне Sepolia.
\\n\\n\\n\\nВ папке migrations создайте новый файл под названием 1_deploy_contract.js и добавьте следующий код:
\\n\\n\\n\\n// Get instance of the NFT contract\\nconst nftContract = artifacts.require(\\\"NftTicketing\\\");\\n\\nmodule.exports = async function (deployer) {\\n // Deploy the contract\\n await deployer.deploy(nftContract);\\n const contract = await nftContract.deployed();\\n\\n // Mint 5 tickets\\n await contract.reserveNfts(5);\\n console.log(\\\"5 NFT Tickets have been minted!\\\")\\n};\\n
\\n\\n\\n\\nВсе готово! Разверните контракт, выполнив следующую команду:
\\n\\n\\n\\ntruffle migrate --network sepolia\\n
\\n\\n\\n\\nЕсли все прошло успешно, вы должны увидеть результат (содержащий адрес контракта), который выглядит примерно так:
\\n\\n\\n\\nStarting migrations...\\n======================\\n> Network name: 'sepolia'\\n> Network id: 5\\n> Block gas limit: 30000000 (0x1c9c380)\\n\\n\\n1_deploy_contract.js\\n====================\\n\\n Deploying 'NftTicketing'\\n -----------------------\\n > transaction hash: …\\n > Blocks: 2 Seconds: 23\\n …\\n > Saving artifacts\\n -------------------------------------\\n > Total cost: 0.1201 ETH\\nSummary\\n=======\\n> Total deployments: 1\\n> Final cost: 0.1201 ETH\\n
\\n\\n\\n\\nВы можете найти адрес вашего контракта на сайте Sepolia etherscan и увидеть его в реальном времени.
\\n\\n\\n\\nПоздравляем! Вы успешно развернули контракт в Sepolia.
\\n\\n\\n\\nУ нас есть наш смарт-контракт! Следующим шагом будет развертывание фронтендов, которые будут взаимодействовать с контрактом и позволят любому человеку вызвать функцию mint, чтобы сделать пожертвование и отчеканить билет для себя.
\\n\\n\\n\\nДля полнофункциональной службы продажи билетов вам, как правило, понадобятся следующие фронтенды:
\\n\\n\\n\\nСоздание этих систем с нуля не входит в задачи данного руководства, но мы оставим вам несколько ресурсов и советов.
\\n\\n\\n\\nЕще один совет: как только у вас есть смарт-контракт и фронтенд (или даже до того, как фронтенд завершен, и вы хотите доказать, что все работает), вы можете использовать Infura NFT API для проверки того, что ваш новый NFT существует. Infura NFT API – это быстрый способ заменить множество кода, связанного с NFT, одним вызовом API.
\\n\\n\\n\\nНапример, информация, необходимая для подтверждения права собственности на наш NFT, легко доступна нам через API. Все, что нам нужно, это адрес кошелька. Код будет выглядеть следующим образом:
\\n\\n\\n\\nconst walletAddress = <your wallet address>\\nconst chainId = \\\"1\\\"\\n\\nconst baseUrl = \\\"https://nft.api.infura.io\\\"\\nconst url = `${baseUrl}/networks/${chainId}/accounts/${walletAddress}/assets/nfts`\\n\\n// API request\\nconst config = {\\n method: 'get',\\n url: url,\\n auth: {\\n username: '<-- INFURA_API_KEY –>',\\n password: '<-- INFURA_API_SECRET –>',\\n }\\n};\\n\\n// API Request\\naxios(config)\\n .then(response => {\\n console.log(response['data'])\\n })\\n .catch(error => console.log('error', error));\\n
\\n\\n\\n\\nRun it …
\\n\\n\\n\\n$ node <filename>.js\\n
\\n\\n\\n\\nИ вы должны увидеть что-то вроде этого:
\\n\\n\\n\\n{\\n total: 1,\\n pageNumber: 1,\\n pageSize: 100,\\n network: 'ETHEREUM',\\n account: <account address>,\\n cursor: null,\\n assets: [\\n {\\n contract: <NFT contract address>,\\n tokenId: '0',\\n supply: '1',\\n type: 'ERC20',\\n metadata: [Object]\\n },\\n …\\n ]\\n}\\n
\\n\\n\\n\\nВ этом руководстве мы развернули полностью функциональный сервис продажи билетов NFT, используя Truffle, Infura и Infura NFT API. Это, конечно, не все, что вам понадобится для того, чтобы разорить Ticketmaster, но это надежное начало и отличное доказательство концепции! Даже если вы не возьмете этот код и не создадите свою собственную платформу для продажи билетов NFT, надеюсь, вы узнали немного о web3 в процессе.
\\n\\n\\n\\r\\nВведение Популярным и практичным примером использования НФТ является генерирование билетов на живые мероприятия. Блокчейн, такой как Ethereum, может гарантировать право собственности, создателя и подлинность цифрового товара, эффективно решая проблему поддельных билетов. Пока крупные игроки, такие как Ticketmaster, борются со скальперами (отчаянно пытающимися контролировать, кто, где и за сколько может перепродавать билеты) и мошенничеством с билетами, […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/postrojte-sistemu-prodazhi-biletov-web3-s-nfts-i-obankrotte-ticketmaster/\"],\"title\":[0,\"Постройте систему продажи билетов Web3 с NFTs и обанкротьте Ticketmaster - Gorlov.\"],\"breadcrumbTitle\":[0,\"Постройте систему продажи билетов Web3 с NFTs и обанкротьте Ticketmaster\"],\"description\":[0,\"Популярным и практичным примером использования НФТ является генерирование билетов на живые мероприятия. Блокчейн, такой как Ethereum, может гарантировать\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Популярным и практичным примером использования НФТ является генерирование билетов на живые мероприятия. Блокчейн, такой как Ethereum, может гарантировать\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Постройте систему продажи билетов Web3 с NFTs и обанкротьте Ticketmaster - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:47:46+00:00\"],\"url\":[0,\"http://igorlov.loc/postrojte-sistemu-prodazhi-biletov-web3-s-nfts-i-obankrotte-ticketmaster/\"]}]}],\"title\":[0,\"Постройте систему продажи билетов Web3 с NFTs и обанкротьте Ticketmaster\"],\"uri\":[0,\"/postrojte-sistemu-prodazhi-biletov-web3-s-nfts-i-obankrotte-ticketmaster/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"postrojte-sistemu-prodazhi-biletov-web3-s-nfts-i-obankrotte-ticketmaster\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Nft\\\"],\\\"id\\\":[0,\\\"dGVybToyMTQ=\\\"],\\\"uri\\\":[0,\\\"/tag/nft/\\\"]}],[0,{\\\"name\\\":[0,\\\"Web3\\\"],\\\"id\\\":[0,\\\"dGVybToxNzE=\\\"],\\\"uri\\\":[0,\\\"/tag/web3/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMjk0\"],\"node\":[0,{\"date\":[0,\"2023-04-23T09:10:34\"],\"content\":[0,\"\\nМаршрутизация – важный аспект веб-приложений, и Next.js имеет мощную систему маршрутизации на основе файловой системы. С выходом Next.js v13.0.0 появилось несколько интересных обновлений в возможностях маршрутизации, которые могут улучшить ваш рабочий процесс разработки. В этой статье мы рассмотрим, что нового в маршрутизации Next.js и как ее использовать с помощью примеров кода.
\\n\\n\\n\\nПрежде чем мы погрузимся в обновления Next.js v13.0.0, давайте рассмотрим основы маршрутизации на базе файловой системы. В Next.js структура каталога ваших страниц определяет структуру URL вашего приложения. Например, если у вас есть файл с именем pages/about.js, URL этой страницы будет example.com/about.
\\n\\n\\n\\nВот пример создания базового приложения Next.js с двумя страницами:
\\n\\n\\n\\n// pages/index.js\\nexport default function Home() {\\n return <h1>Welcome to my homepage!</h1>\\n}\\n\\n// pages/about.js\\nexport default function About() {\\n return <h1>About me</h1>\\n}\\n\\n
\\n\\n\\n\\nОдним из значительных обновлений в Next.js v13.0.0 является введение маршрутов catch-all. Эта функция позволяет создавать динамические маршруты, соответствующие любой структуре URL.
\\n\\n\\n\\nВот пример использования маршрутов catch-all в вашем приложении Next.js:
\\n\\n\\n\\n// pages/[...slug].js\\nimport { useRouter } from 'next/router'\\n\\nexport default function DynamicRoute() {\\n const router = useRouter()\\n const { slug } = router.query\\n\\n return (\\n <>\\n <h1>{slug}</h1>\\n </>\\n )\\n}\\n\\n
\\n\\n\\n\\nВ приведенном примере мы определяем динамический маршрут с помощью синтаксиса […], а параметр slug представляет собой массив, содержащий все сегменты пути URL. Затем мы используем хук useRouter для доступа к объекту маршрутизатора и извлекаем параметр slug из объекта запроса. Наконец, мы отображаем параметр slug в теге h1.
\\n\\n\\n\\nС помощью маршрутов catch-all вы можете создавать гибкие и динамические структуры URL для вашего приложения.
\\n\\n\\n\\nЕще одним важным обновлением в Next.js v13.0.0 является введение страниц возврата. Эта функция позволяет создать пользовательскую страницу 404, которая генерируется динамически на основе запрошенного URL.
\\n\\n\\n\\nВот пример того, как создать страницу отката в приложении Next.js:
\\n\\n\\n\\n// pages/404.js\\nimport { useRouter } from 'next/router'\\n\\nexport default function Custom404() {\\n const router = useRouter()\\n\\n return (\\n <>\\n <h1>404 - Page Not Found</h1>\\n <p>The requested URL {router.asPath} was not found.</p>\\n </>\\n )\\n}\\n\\n
\\n\\n\\n\\nВ приведенном выше примере мы определяем пользовательскую страницу 404 в файле pages/404.js. Мы используем хук useRouter для доступа к объекту маршрутизатора и извлекаем запрашиваемый URL с помощью свойства asPath. Наконец, мы выводим пользовательское сообщение с запрошенным URL.
\\n\\n\\n\\nКомпонент Link является важной частью маршрутизации Next.js, и теперь он включает в себя возможность предварительной выборки. Эта функция повышает производительность вашего приложения за счет предварительной выборки связанной страницы в фоновом режиме.
\\n\\n\\n\\nВот пример использования компонента Link с предварительной выборкой:
\\n\\n\\n\\n// pages/index.js\\nimport Link from 'next/link'\\n\\nexport default function Home() {\\n return (\\n <>\\n <h1>Welcome to my homepage!</h1>\\n <Link href=\\\"/about\\\" prefetch>\\n <a>About</a>\\n </Link>\\n </>\\n )\\n }\\n\\n
\\n\\n\\n\\nВ приведенном выше примере мы импортируем компонент Link из Next.js и оборачиваем им тег a
. Мы устанавливаем параметр href в /about и параметр prefetch в true. Таким образом, когда пользователь наводит курсор на ссылку, в фоновом режиме происходит предварительная загрузка страницы /about, что может улучшить пользовательский опыт за счет сокращения времени загрузки страницы.
Именованные маршруты — это еще одно обновление в Next.js v13.0.0, которое позволяет вам определять пользовательские имена для ваших маршрутов. Эта функция полезна для создания ссылок на динамические маршруты.
\\n\\n\\n\\nВот пример того, как определить и использовать именованные маршруты в приложении Next.js:
\\n\\n\\n\\n// pages/[slug].js\\nimport Link from 'next/link'\\n\\nexport default function DynamicRoute() {\\n return (\\n <>\\n <h1>Welcome to my dynamic route!</h1>\\n <Link href=\\\"/[slug]\\\" as=\\\"/blog/post-1\\\">\\n <a>Post 1</a>\\n </Link>\\n <Link href=\\\"/[slug]\\\" as=\\\"/blog/post-2\\\">\\n <a>Post 2</a>\\n </Link>\\n </>\\n )\\n}\\n\\n
\\n\\n\\n\\nВ приведенном выше примере мы определяем динамический маршрут с параметром [slug]. Мы используем компонент Link для создания ссылок на динамический маршрут и устанавливаем параметр as в пользовательское имя маршрута. Это позволяет нам создавать удобные для пользователя ссылки на динамические маршруты.
\\n\\n\\n\\nМаршрутизация — важный аспект разработки веб-приложений, и Next.js предоставляет мощную систему маршрутизации на основе файловой системы, которая позволяет создавать гибкие и динамические структуры URL. Благодаря обновлениям в Next.js v13.0.0 вы можете создавать маршруты с подхватом, страницы возврата, ссылки с предварительной выборкой и именованные маршруты, что позволяет улучшить пользовательский опыт и упростить рабочий процесс разработки.
\\n\\n\\n\\nВ этой статье мы рассмотрели обновления в Next.js v13.0.0 и привели примеры кода, которые помогут вам начать работу с маршрутизацией Next.js. Используя возможности маршрутизации Next.js, вы сможете создавать надежные и удобные веб-приложения.
\\n\\n\\n\\r\\nМаршрутизация – важный аспект веб-приложений, и Next.js имеет мощную систему маршрутизации на основе файловой системы. С выходом Next.js v13.0.0 появилось несколько интересных обновлений в возможностях маршрутизации, которые могут улучшить ваш рабочий процесс разработки. В этой статье мы рассмотрим, что нового в маршрутизации Next.js и как ее использовать с помощью примеров кода. Маршрутизация на основе файловой […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Как закодить\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/kak-zakodit/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/issledovanie-marshrutizaczii-next-js-obnovleniya-v-versii-13-0-0/\"],\"title\":[0,\"Исследование маршрутизации Next.js: Обновления в версии 13.0.0" - Gorlov.\"],\"breadcrumbTitle\":[0,\"Исследование маршрутизации Next.js: Обновления в версии 13.0.0″\"],\"description\":[0,\"Маршрутизация - важный аспект веб-приложений, и Next.js имеет мощную систему маршрутизации на основе файловой системы. С выходом Next.js v13.0.0 появилось\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Маршрутизация - важный аспект веб-приложений, и Next.js имеет мощную систему маршрутизации на основе файловой системы. С выходом Next.js v13.0.0 появилось\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Исследование маршрутизации Next.js: Обновления в версии 13.0.0" - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:47:57+00:00\"],\"url\":[0,\"http://igorlov.loc/issledovanie-marshrutizaczii-next-js-obnovleniya-v-versii-13-0-0/\"]}]}],\"title\":[0,\"Исследование маршрутизации Next.js: Обновления в версии 13.0.0″\"],\"uri\":[0,\"/issledovanie-marshrutizaczii-next-js-obnovleniya-v-versii-13-0-0/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"issledovanie-marshrutizaczii-next-js-obnovleniya-v-versii-13-0-0\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Next.js\\\"],\\\"id\\\":[0,\\\"dGVybToxNTA=\\\"],\\\"uri\\\":[0,\\\"/tag/next-js/\\\"]}],[0,{\\\"name\\\":[0,\\\"React\\\"],\\\"id\\\":[0,\\\"dGVybTozNg==\\\"],\\\"uri\\\":[0,\\\"/tag/react/\\\"]}],[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"id\\\":[0,\\\"dGVybToyNg==\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMjk3\"],\"node\":[0,{\"date\":[0,\"2023-04-23T08:45:21\"],\"content\":[0,\"\\nPrisma требует, чтобы экземпляр MongoDB работал как набор реплик, который нетривиально настроить локально, хотя довольно легко с помощью Atlas. Но если ваше интернет-соединение не очень хорошее, установка локального экземпляра становится обязательной для разработки.
\\n\\n\\n\\nПочему я пишу эту статью? Потому что существует слишком много способов сделать это неправильно. Я нашел правильный способ тяжелым путем. Если вам нужно быстрое решение, вы можете проверить мой ответ на stackoverflow. Здесь я собираюсь предоставить подробные шаги с объяснениями.
\\n\\n\\n\\nНабор реплик в MongoDB – это группа процессов mongodb, которые обрабатывают одни и те же данные для обеспечения избыточности.
\\n\\n\\n\\nИзбыточность повышает высокую доступность. В наборе реплик, если один узел выходит из строя, другой узел может взять на себя выполнение операций.
\\n\\n\\n\\nСуществует один первичный узел и другие вторичные узлы. Вот диаграмма, которая суммирует взаимодействие между первичными и вторичными узлами
\\n\\n\\n\\nЕсли вы хотите узнать больше, вы можете прочитать об этом здесь
\\n\\n\\n\\nPrisma опубликовала образ docker, который создает реплику одного экземпляра без дополнительной настройки.
\\n\\n\\n\\nПерейдите к тегам и найдите последнюю доступную версию, на данный момент это 5.0.3.
\\n\\n\\n\\nИзвлеките образ docker с помощью
\\n\\n\\n\\ndocker pull prismagraphql/mongo-single-replica:5.0.3\\n
\\n\\n\\n\\nЗапустите образ с помощью этой команды
\\n\\n\\n\\ndocker run --name mongo \\\\\\n -p 27017:27017 \\\\\\n -e MONGO_INITDB_ROOT_USERNAME=\\\"monty\\\" \\\\\\n -e MONGO_INITDB_ROOT_PASSWORD=\\\"pass\\\" \\\\\\n -d prismagraphql/mongo-single-replica:5.0.3\\n
\\n\\n\\n\\nТеперь необходимо настроить URL подключения, в данном случае он должен выглядеть следующим образом,
\\n\\n\\n\\nDATABASE_URL=\\\"mongodb://monty:pass@localhost:27017/db_name?authSource=admin&directConnection=true\\\"\\n
\\n\\n\\n\\nЗамените db_name на имя вашей базы данных, если она не существует, она будет создана автоматически.
\\n\\n\\n\\nОбратите внимание, что ROOT_USERNAME и authSource не совпадфают, вы можете изменить ROOT_USERNAME на любой другой, но authSouce должен быть admin, так как это база данных, которая содержит учетные данные пользователей.
\\n\\n\\n\\nНаконец, протестируйте соединение. Выполните эту команду в корневом каталоге проекта. Это синхронизирует вашу схему с базой данных
\\n\\n\\n\\nnpx prisma db push\\n
\\n\\n\\n\\nВыберите свободный кластер M0 и задайте ему имя.
Вам будет предложено создать пользователя
ПРИМЕЧАНИЕ: Для безопасности вы должны использовать автоматически сгенерированный пароль и записать его где-нибудь.
Ваш IP будет автоматически добавлен в список доступа, нажмите “Завершить и закрыть” и перейдите к базе данных.
Чтобы получить url подключения, нажмите кнопку “Connect” и выберите “Drivers”.
Наконец, скопируйте url подключения и замените на ваш автоматически сгенерированный пароль
ВАЖНО: Вы должны добавить имя базы данных после имени хоста. В моем случае url выглядит следующим образом
\\n\\n\\n\\nDATABASE_URL=\\\"mongodb+srv://monty:pass@cluster0.nqtl0pv.mongodb.net/db_name?retryWrites=true&w=majority\\\"\\n
\\n\\n\\n\\nПротестируйте соединение и схему синхронизации
\\n\\n\\n\\nnpx prisma db push\\n
\\n\\n\\n\\r\\nОбзор Prisma требует, чтобы экземпляр MongoDB работал как набор реплик, который нетривиально настроить локально, хотя довольно легко с помощью Atlas. Но если ваше интернет-соединение не очень хорошее, установка локального экземпляра становится обязательной для разработки. Почему я пишу эту статью? Потому что существует слишком много способов сделать это неправильно. Я нашел правильный способ тяжелым путем. Если […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/nastrojka-nabora-replik-mongodb-lokalno-v-docker-ili-s-pomoshhyu-atlas-dlya-prisma-orm/\"],\"title\":[0,\"Настройка набора реплик MongoDB локально в Docker или с помощью Atlas для Prisma ORM - Gorlov.\"],\"breadcrumbTitle\":[0,\"Настройка набора реплик MongoDB локально в docker или с помощью Atlas для Prisma ORM\"],\"description\":[0,\"Prisma требует, чтобы экземпляр MongoDB работал как набор реплик, который нетривиально настроить локально, хотя довольно легко с помощью Atlas. Но если ваше\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Prisma требует, чтобы экземпляр MongoDB работал как набор реплик, который нетривиально настроить локально, хотя довольно легко с помощью Atlas. Но если ваше\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Настройка набора реплик MongoDB локально в Docker или с помощью Atlas для Prisma ORM - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:48:05+00:00\"],\"url\":[0,\"http://igorlov.loc/nastrojka-nabora-replik-mongodb-lokalno-v-docker-ili-s-pomoshhyu-atlas-dlya-prisma-orm/\"]}]}],\"title\":[0,\"Настройка набора реплик MongoDB локально в docker или с помощью Atlas для Prisma ORM\"],\"uri\":[0,\"/nastrojka-nabora-replik-mongodb-lokalno-v-docker-ili-s-pomoshhyu-atlas-dlya-prisma-orm/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"nastrojka-nabora-replik-mongodb-lokalno-v-docker-ili-s-pomoshhyu-atlas-dlya-prisma-orm\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Mongodb\\\"],\\\"id\\\":[0,\\\"dGVybToyMTM=\\\"],\\\"uri\\\":[0,\\\"/tag/mongodb/\\\"]}],[0,{\\\"name\\\":[0,\\\"Prisma\\\"],\\\"id\\\":[0,\\\"dGVybToxODg=\\\"],\\\"uri\\\":[0,\\\"/tag/prisma/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMzEy\"],\"node\":[0,{\"date\":[0,\"2023-04-23T08:36:13\"],\"content\":[0,\"\\nДобро пожаловать в нашу серию уроков по языку программирования Go! В этой серии мы глубоко погрузимся в мир языка Go и изучим его возможности, синтаксис и лучшие практики для создания масштабируемых и эффективных приложений. Независимо от того, являетесь ли вы новичком в программировании или опытным разработчиком, эта серия предоставит вам знания и инструменты, необходимые для создания надежных приложений на Go.
\\n\\n\\n\\nНа протяжении всего цикла мы рассмотрим основы языка Go, включая переменные, функции и управляющие структуры, а также более сложные темы, такие как параллелизм, работа в сети и доступ к базам данных. Мы также приведем практические примеры и упражнения, чтобы помочь вам лучше понять язык и его концепции.
\\n\\n\\n\\nНаша цель — сделать эту серию доступной и увлекательной для слушателей любого уровня. Если вы только начинаете программировать или хотите расширить свои навыки с помощью нового языка, мы надеемся, что эта серия даст вам ценные знания и инструменты для достижения успеха.
\\n\\n\\n\\nИтак, без лишних слов, давайте окунемся в мир Go и начнем изучать его уникальные возможности и преимущества.
\\n\\n\\n\\nGo, также известный как Golang, — это популярный язык программирования, созданный компанией Google в 2007 году. За прошедшие годы он привлек большое внимание благодаря своей простоте, эффективности и масштабируемости. В этой статье мы подробно рассмотрим, что такое Go, его историю и уникальные особенности, которые делают его популярным среди разработчиков по всему миру.
\\n\\n\\n\\nGo – это язык программирования с открытым исходным кодом, разработанный компанией Google в 2007 году. Он был создан Робертом Грисемером, Робом Пайком и Кеном Томпсоном, которые на тот момент были опытными разработчиками. Язык был разработан как простой, эффективный и масштабируемый, что делает его пригодным для широкого круга приложений.
\\n\\n\\n\\nGo имеет несколько уникальных особенностей, которые выделяют его среди других языков программирования. Некоторые из этих особенностей включают:
\\n\\n\\n\\nНекоторые из преимуществ использования Go включают в себя:
\\n\\n\\n\\nНесмотря на свои многочисленные преимущества, Go имеет несколько недостатков, в том числе:
\\n\\n\\n\\nСоздание Go стало ответом на проблемы, с которыми столкнулась компания Google при создании крупномасштабных приложений. Google использовал комбинацию языков, включая C++, Python и Java, но ни один из этих языков не обеспечивал эффективности, простоты и масштабируемости, которые были необходимы Google.
\\n\\n\\n\\nВ 2007 году Роберт Гризмер, Роб Пайк и Кен Томпсон начали работу над новым языком, который бы решал эти проблемы. Их целью было создать язык, который был бы таким же быстрым, как C++, таким же простым в написании, как Python, и таким же масштабируемым, как Java. Первая версия Go была выпущена в 2009 году, и с тех пор она пережила несколько крупных релизов.
\\n\\n\\n\\nС момента своего выхода Go обрел большое и активное сообщество разработчиков, которые используют язык для создания широкого спектра приложений, от веб-серверов и API до системных утилит и мобильных приложений. Go используется многими крупными компаниями, включая Google, Uber, Dropbox и Docker.
\\n\\n\\n\\nСообщество Go известно своей культурой сотрудничества и поддержки, многие разработчики вносят свой вклад в проекты с открытым исходным кодом и помогают друг другу решать проблемы. Сообщество Go также известно своей приверженностью к простоте и эффективности, многие разработчики сосредоточены на создании приложений, которые являются быстрыми, масштабируемыми и простыми в обслуживании.
\\n\\n\\n\\nGo – это мощный и популярный язык программирования, который предлагает множество уникальных возможностей и преимуществ. Его простота, эффективность и масштабируемость делают его идеальным выбором для создания широкого спектра приложений, от веб-серверов и API до системных утилит и мобильных приложений. Хотя у него есть несколько недостатков, таких как отсутствие библиотек и кривая обучения для начинающих разработчиков, его активное сообщество и стремление к простоте и эффективности делают этот язык достойным рассмотрения для вашего следующего проекта.
\\n\\n\\n\\nВ следующей части этой серии мы собираемся настроить среду разработки Go, так что, если вы не хотите пропустить эту статью, то следите за нами и будьте в курсе наших последних событий.
\\n\\n\\n\\r\\nДобро пожаловать в нашу серию уроков по языку программирования Go! В этой серии мы глубоко погрузимся в мир языка Go и изучим его возможности, синтаксис и лучшие практики для создания масштабируемых и эффективных приложений. Независимо от того, являетесь ли вы новичком в программировании или опытным разработчиком, эта серия предоставит вам знания и инструменты, необходимые для […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/yazyk-programmirovaniya-go-ischerpyvayushhee-vvedenie-i-istoriya-revolyuczionnogo-yazyka-google/\"],\"title\":[0,\"Язык программирования Go: Исчерпывающее введение и история революционного языка Google - Gorlov.\"],\"breadcrumbTitle\":[0,\"Язык программирования Go: Исчерпывающее введение и история революционного языка Google\"],\"description\":[0,\"Добро пожаловать в нашу серию уроков по языку программирования Go! В этой серии мы глубоко погрузимся в мир языка Go и изучим его возможности, синтаксис и\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Добро пожаловать в нашу серию уроков по языку программирования Go! В этой серии мы глубоко погрузимся в мир языка Go и изучим его возможности, синтаксис и\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Язык программирования Go: Исчерпывающее введение и история революционного языка Google - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:48:15+00:00\"],\"url\":[0,\"http://igorlov.loc/yazyk-programmirovaniya-go-ischerpyvayushhee-vvedenie-i-istoriya-revolyuczionnogo-yazyka-google/\"]}]}],\"title\":[0,\"Язык программирования Go: Исчерпывающее введение и история революционного языка Google\"],\"uri\":[0,\"/yazyk-programmirovaniya-go-ischerpyvayushhee-vvedenie-i-istoriya-revolyuczionnogo-yazyka-google/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"yazyk-programmirovaniya-go-ischerpyvayushhee-vvedenie-i-istoriya-revolyuczionnogo-yazyka-google\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Go\\\"],\\\"id\\\":[0,\\\"dGVybToyMTI=\\\"],\\\"uri\\\":[0,\\\"/tag/go/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMzAx\"],\"node\":[0,{\"date\":[0,\"2023-04-23T08:24:48\"],\"content\":[0,\"\\nСейчас все говорят о ChatGPT. Исключительно умный ИИ продолжает поражать интернет даже спустя пару месяцев после своего выхода. Наличие ChatGPT на сайте – это здорово, но настоящее веселье начинается, когда вы получаете доступ к API. Это дает вам прекрасную возможность интегрировать интеллектуальный ИИ в свои проекты и приложения, чтобы сделать их более мощными и внедрить удивительные функции.
\\n\\n\\n\\nВ этой статье мы расскажем вам о том, как создать собственного Telegram-бота и интегрировать с ним ChatGPT, используя библиотеку OpenAI для Python. Это может показаться простейшей задачей, но давайте немного оживим ее, введя команду summarize, которая позволит вам получить сводку нескольких сообщений в чате.
\\n\\n\\n\\nВ этом посте предполагается, что у вас есть базовые знания Python. Однако я рекомендую ознакомиться с треками Hyperskill по Python и Flask, чтобы узнать больше о Python и разработке веб-приложений с помощью Flask.
\\n\\n\\n\\nПрежде чем приступить к работе над кодом, вам нужно будет выполнить некоторые подготовительные действия, чтобы получить все необходимые доступы.
\\n\\n\\n\\nСохраните эти секретные строки и охраняйте их своей жизнью. Никто посторонний не должен получить к ним доступ: это может привести к нарушению безопасности.
\\n\\n\\n\\nПримечание: полный финальный код проекта (разбитый на этапы с коммитами) доступен на моем GitHub, за подробностями обращайтесь сюда: https://github.com/yellalena/telegram-gpt-summarizer.
\\n\\n\\n\\nБиблиотеки Python, которые необходимо установить для этого шага: flask, pydantic, requests и pyngrok.
\\n\\n\\n\\nДавайте начнем с написания кода для самого базового бота Telegram. Он должен получать сообщения из чата и уметь отвечать на них.
Первым делом создайте каталог для вашего проекта и инициализируйте виртуальную среду Python. Кстати, если вы используете PyCharm, он создаст виртуальную среду за вас.
На данном этапе цель разделена на четыре части:
\\n\\n\\n\\nВот как выглядит main.py на этом этапе:
\\n\\n\\n\\napp = Flask(__name__)\\n\\n@app.route(\\\"https://dev.to/\\\", methods=[\\\"GET\\\", \\\"POST\\\"])\\ndef handle_webhook():\\n update = Update(**request.json)\\n chat_id = update.message.chat.id\\n\\n response = f\\\"This is a response for message: {update.message.text}\\\"\\n app.bot.send_message(chat_id, response)\\n\\n return \\\"OK\\\", 200\\n\\ndef run_ngrok(port=8000):\\n http_tunnel = ngrok.connect(port)\\n return http_tunnel.public_url\\n\\ndef main():\\n app.bot = TelegramBot(Config.TELEGRAM_TOKEN)\\n host = run_ngrok(Config.PORT)\\n app.bot.set_webhook(host)\\n app.run(port=Config.PORT, debug=True, use_reloader=False)\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n
\\n\\n\\n\\nНесколько моментов требуют разъяснения:
\\n\\n\\n\\nЧтобы установить webhook, вам нужно отправить запрос на адрес API telegram бота, созданный с использованием полученного секретного токена. Код бота telegram выглядит следующим образом:
\\n\\n\\n\\nimport requests\\nfrom config import Config\\n\\nclass TelegramBot:\\n def __init__(self, token):\\n self.token = token\\n self.bot_api_url = f\\\"{Config.TELEGRAM_API}/bot{self.token}\\\"\\n\\n def set_webhook(self, host):\\n host = host.replace(\\\"http\\\", \\\"https\\\")\\n set_webhook_url = f\\\"{self.bot_api_url}/setWebhook?url={host}\\\"\\n response = requests.get(set_webhook_url)\\n response.raise_for_status()\\n\\n def send_message(self, chat_id, message):\\n send_message_url = f\\\"{self.bot_api_url}/sendMessage\\\"\\n response = requests.post(send_message_url, json={\\\"chat_id\\\": chat_id,\\n \\\"text\\\": message})\\n response.raise_for_status()\\n
\\n\\n\\n\\nТеперь, когда все готово (не забудьте, что я опустил часть основного кода, вы можете найти его в репозитории), экспортируйте токен вашего бота в переменную окружения и нажмите кнопку “Run”!
\\n\\n\\n\\n
Ура! Он жив!
Удивительно, но следующим шагом теперь должно стать добавление щепотки интеллекта нашему умному боту. Установите официальную либу OpenAI для Python с помощью pip: pip install openai.
После этого мы сможем создать класс-помощник для общения с ИИ.
import openai\\n\\nclass OpenAiHelper:\\n def __init__(self, token, model=\\\"gpt-3.5-turbo\\\"):\\n openai.api_key = token\\n self.model = model\\n\\n def get_response(self, message_text):\\n response = openai.ChatCompletion.create(model=self.model,\\n messages=[{\\\"role\\\": \\\"user\\\", \\\"content\\\": message_text}])\\n return response.choices[0].message.content\\n
\\n\\n\\n\\nAPI предлагает множество моделей, которые можно использовать для вашего проекта. Самыми популярными, конечно, являются GPT. В последнее время больше всего шума наделал GPT-4, но (и из-за этого) доступ к нему сейчас ограничен, поэтому для удобства тестирования я выбрал GPT-3. Ничего страшного, вы всегда можете выбрать тот, который вам больше нравится, просто измените имя строки, которое вы передаете помощнику.
\\n\\n\\n\\nНе забудьте добавить свойство OPENAI_TOKEN в конфиг и давайте используем хелпер в коде.
\\n\\n\\n\\nСначала, конечно, инстанцируем его в методе main():
\\n\\n\\n\\nА затем вызовите его из функции представления, вот так:
\\n\\n\\n\\nresponse = app.openai_helper.get_response(update.message.text)\\n
\\n\\n\\n\\nУх! Волшебство свершилось!
\\n\\n\\n\\nБиблиотеки Python, которые необходимо установить для этого шага: quart, telethon.
\\n\\n\\n\\nНаверняка у вас такое бывало – вас добавили в чат с группой друзей, которые любят обсуждать интересные вещи или делиться какими-то новостями или идеями. У вас было много дел, и вы пропустили все веселье в чате. Следующее, что вы видите – сотню непрочитанных сообщений. Разве не было бы здорово, если бы кто-то мог дать вам краткий обзор того, что там произошло, вместо того, чтобы читать все это? Ну, GPT, конечно, может это сделать. Нужно только попросить его об этом.
\\n\\n\\n\\nВот тут-то и начинается самое интересное. По какой-то причине API ботов Telegram не позволяет ботам читать историю разговоров. У нас есть веб-крючки и явный метод GetUpdates(), но они работают, только если кто-то упомянул бота. Другой вариант – сделать так, чтобы бот получал все обновления, если он добавлен как администратор, но у этого подхода тоже есть несколько минусов. Во-первых, вам придется создать целое хранилище для сообщений. Во-вторых, что если вы хотите подытожить разговор, который шел до того, как бот был добавлен в чат? Не наш случай.
\\n\\n\\n\\nОчевидно, это не повод опускать руки. Telegram предоставляет Core API, и он может помочь с извлечением истории чата. Единственное, что он асинхронный. И самая популярная библиотека Python для него, Telethon, тоже асинхронная. А Flask – синхронная. О-о-о.
\\n\\n\\n\\nИ вот тут-то на сцену выходит загадочный Quart, упомянутый в заголовке. Quart – это API Flask, реализованный с использованием асинхронности, ожидания и веб-сервера ASGI (а не синхронности и WSGI). Его главное преимущество в нашем случае заключается в том, что синтаксис в основном тот же. Давайте проведем быструю реорганизацию кода.
\\n\\n\\n\\nИзменения просты. Во-первых, настройте импорт и замените каждую колбу на кварту:
Затем сделайте все методы веб-приложения асинхронными. И ожидайте все свойства и методы, которые теперь стали асинхронными:
Если вы не уверены в том, что такое async Python, я советую вам ознакомиться с этой частью документации Telethon по основам asyncio.
\\n\\n\\n\\nЯ также переместил ngrok и TelegramBot, чтобы запустить их в отдельном методе, украшенном @app.before_serving. Это встроенный в Quart декоратор, который гарантирует, что все внутри этого метода будет запущено до того, как веб-приложение будет запущено и будет обслуживаться. Он необходим для того, чтобы бот и помощник инициализировались в том же цикле событий, что и основное приложение.
\\n\\n\\n\\n@app.before_serving\\nasync def startup():\\n host = run_ngrok(Config.PORT)\\n app.bot = TelegramBot(Config.TELEGRAM_TOKEN)\\n app.bot.set_webhook(host)\\n app.openai_helper = OpenAiHelper(Config.OPENAI_TOKEN)\\n
\\n\\n\\n\\nЗапуск приложения тоже немного изменился, но не сильно. Hypercorn – это ASGI-сервер, используемый для асинхронного запуска Quart, и если мы хотим указать порт приложения, нам нужно сделать это в конфиге. Обратите внимание, что main() теперь тоже асинхронный и выполняется с помощью asyncio:
\\n\\n\\n\\nasync def main():\\n quart_cfg = hypercorn.Config()\\n quart_cfg.bind = [f\\\"127.0.0.1:{Config.PORT}\\\"]\\n await hypercorn.asyncio.serve(app, quart_cfg)\\n\\nif __name__ == \\\"__main__\\\":\\n asyncio.run(main())\\n
\\n\\n\\n\\nВот и все. Давайте проверим, прошли ли изменения гладко для нашего бота. Запуск, текст, ввод:
Он говорит. Отлично. Теперь получим историю чата, чтобы ИИ подвел итог. Воспользуемся Core API Telegram с помощью либы Telethon. Там нам понадобятся две последние секретные строки, которые у вас есть – экспортируйте их как переменные окружения.
\\n\\n\\n\\nTelegramBot имеет небольшие изменения в методе __init__: он должен иметь новое свойство core_api_client, которое инициализирует клиент Telethon, и, конечно, вам нужно передать секреты Core API в качестве аргументов.
\\n\\n\\n\\nИ этот крошечный метод будет отвечать за извлечение истории:
\\n\\n\\n\\nasync def get_chat_history(self, chat_id, limit=30):\\n if not self.core_api_client:\\n return []\\n history = await self.core_api_client.get_messages(chat_id, limit)\\n result = [f\\\"{message.sender.first_name} {message.sender.last_name}: {message.message} \\\\n\\\"\\n for message in history if not message.action]\\n result.reverse()\\n return '\\\\n'.join(result)\\n
\\n\\n\\n\\nУ функции get_messages в Telethon есть еще много различных параметров, которые можно передавать помимо параметров ограничения. Например, он может отменить историю или ограничить ее по дате, а не по количеству сообщений. С этим интересно играть, и вы можете настроить своего бота так, как вам захочется.
\\n\\n\\n\\nНу, мы почти закончили. Осталось добавить опцию подведения итогов в обработчик webhook. Вот как выглядит получение ответа:
\\n\\n\\n\\n# process \\\"summarize\\\" command\\n if update.message.text.startswith(\\\"/summarize\\\"):\\n history = await app.bot.get_chat_history(chat_id)\\n response = app.openai_helper.get_response(\\\"Please, briefly summarize the following conversation history:\\\\n\\\" +\\\\\\n history)\\n else:\\n response = app.openai_helper.get_response(update.message.text)\\n\\n app.bot.send_message(chat_id, response)\\n
\\n\\n\\n\\nДавайте посмотрим, как он расцветает!
\\n\\n\\n\\nПосле того как вы запустите приложение в первый раз, оно попросит вас войти в Telegram. Это нормально: это необходимо для получения доступа к истории сообщений и другим приватным данным, которые может предложить нам Core API. Введите тот же номер телефона, который вы использовали для получения доступа к Telegram Core API. Вы получите проверочный код внутри приложения, после чего все будет готово.
\\n\\n\\n\\nДобавьте бота в разговор с друзьями и попросите подвести итоги:
\\n\\n\\n\\nВот и все! Вы можете продолжить бесконечный список вещей: добавить обработку других типов сообщений, кроме текстовых, настроить количество сообщений для суммирования из чата и т.д. Дерзайте и не забудьте выложить свой код на GitHub. Счастливого кодинга! 🙂
\\n\\n\\n\\nНе забудьте зайти на сайт Hyperskill, чтобы продолжить изучение разработки веб-приложений с помощью Python и Flask. Вот ссылки на некоторые темы, которые могут пригодиться вам именно для этого проекта:
\\n\\n\\n\\nОбработчики ошибок: Если вы не обработаете ошибки должным образом, велика вероятность того, что ваше приложение не будет работать во время выполнения или покажет пользователю некрасивые трассировки. Чтобы избежать этого, прочитайте о типах ошибок и о том, как их лучше всего обрабатывать в приложении Flask.
\\n\\n\\n\\nЛогирование: Это одна из самых важных вещей, когда речь идет о тестировании и отладке вашего приложения. Составление содержательных и читабельных журналов – обязательное условие для разработчика программного обеспечения. Ознакомьтесь с этой темой, чтобы узнать, как выполнять логирование в Python.
\\n\\n\\n\\nЗнакомство с SQLAlchemy: Когда вы решите, что хотите хранить некоторые данные приложения, будь то информация о пользователе или история разговоров, вам нужно будет связаться с базой данных. Эта тема познакомит вас с основами SQLAlchemy, которая делает работу с базами данных простой и удобной.
\\n\\n\\n\\nHyperskill – это платформа обучения на основе проектов, которая предлагает персонализированную учебную программу и разнообразные направления, чтобы помочь людям с разным уровнем подготовки получить актуальные для рынка навыки через онлайн-образование. Она не только дает вам прочные теоретические знания, но и позволяет сразу же отработать навыки на практике – а практика делает обучение идеальным.
\\n\\n\\n\\nВы нашли этот пост полезным? Нажимайте на кнопку и следуйте за Hyperskill и мной, чтобы прочитать больше о них позже 🙂
\\n\\n\\n\\r\\nСейчас все говорят о ChatGPT. Исключительно умный ИИ продолжает поражать интернет даже спустя пару месяцев после своего выхода. Наличие ChatGPT на сайте – это здорово, но настоящее веселье начинается, когда вы получаете доступ к API. Это дает вам прекрасную возможность интегрировать интеллектуальный ИИ в свои проекты и приложения, чтобы сделать их более мощными и внедрить […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/bot-dlya-kratkogo-izlozheniya-razgovorov-v-telegram-s-chatgpt-i-flask-quart/\"],\"title\":[0,\"Бот для краткого изложения разговоров в Telegram с ChatGPT и Flask (Quart) - Gorlov.\"],\"breadcrumbTitle\":[0,\"Бот для краткого изложения разговоров в Telegram с ChatGPT и Flask (Quart)\"],\"description\":[0,\"Сейчас все говорят о ChatGPT. Исключительно умный ИИ продолжает поражать интернет даже спустя пару месяцев после своего выхода. Наличие ChatGPT на сайте - это\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Сейчас все говорят о ChatGPT. Исключительно умный ИИ продолжает поражать интернет даже спустя пару месяцев после своего выхода. Наличие ChatGPT на сайте - это\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Бот для краткого изложения разговоров в Telegram с ChatGPT и Flask (Quart) - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:48:25+00:00\"],\"url\":[0,\"http://igorlov.loc/bot-dlya-kratkogo-izlozheniya-razgovorov-v-telegram-s-chatgpt-i-flask-quart/\"]}]}],\"title\":[0,\"Бот для краткого изложения разговоров в Telegram с ChatGPT и Flask (Quart)\"],\"uri\":[0,\"/bot-dlya-kratkogo-izlozheniya-razgovorov-v-telegram-s-chatgpt-i-flask-quart/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"bot-dlya-kratkogo-izlozheniya-razgovorov-v-telegram-s-chatgpt-i-flask-quart\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Flask\\\"],\\\"id\\\":[0,\\\"dGVybToyMTE=\\\"],\\\"uri\\\":[0,\\\"/tag/flask/\\\"]}],[0,{\\\"name\\\":[0,\\\"Python\\\"],\\\"id\\\":[0,\\\"dGVybTo2NA==\\\"],\\\"uri\\\":[0,\\\"/tag/python/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMzA3\"],\"node\":[0,{\"date\":[0,\"2023-04-22T07:45:11\"],\"content\":[0,\"\\nЕсли вы читаете эту статью, значит, вы управляете идентификационными данными пользователей за пределами AWS и используете Identity Provider (IdP) Federation для предоставления этим внешним идентификационным данным разрешения на использование ресурсов AWS в вашей учетной записи.
\\n\\n\\n\\nВ этом процессе аутентификации одной из наиболее распространенных ошибок, с которой вам, возможно, придется столкнуться, является “response did not contain a valid saml assertion”, и в этой статье я хочу поделиться с вами некоторыми советами по устранению неполадок, чтобы решить эту проблему.
\\n\\n\\n\\nЕсли вы работаете на AWS (но в целом), недействительное утверждение SAML в основном возникает, когда в SAML-ответе от IdP отсутствует атрибут с именем Name, установленным в https://aws.amazon.com/SAML/Attributes/Role . Атрибут также должен содержать один или несколько элементов AttributeValue, каждый из которых содержит эти две строки, разделенные запятой:
\\n\\n\\n\\nНапример:
\\n\\n\\n\\n<Attribute Name=\\\"<https://aws.amazon.com/SAML/Attributes/Role>\\\">\\n\\n<AttributeValue>arn:aws:iam::account-number:role/role-name1,arn:aws:iam::account-number:saml-provider/provider-name</AttributeValue>\\n\\n</Attribute>\\n
\\n\\n\\n\\nЗдесь также приведен пример разрешения для Okta.
\\n\\n\\n\\nhttps://docs.pulsesecure.net/WebHelp/Content/PCS/PCS_AdminGuide_8.2/Investigating a No valid assertion.htm
\\n\\n\\n\\nЕсли часы SAML IdP и поставщика услуг SAML (например, AWS) не синхронизированы, утверждение может быть признано недействительным, и аутентификация не пройдет.
\\n\\n\\n\\nВозможное решение – проверить, что ваш IdP и поставщик услуг могут использовать один и тот же NTP-сервер, или доказать, что часы вашего сервера актуальны.
\\n\\n\\n\\nОбновление данных и модернизация могут привести к тому, что сертификаты перестанут доверять одной стороне процесса федерации или другой. Попробуйте проверить и обновить metadata.xml с обеих сторон, чтобы сертификаты снова совпадали.
\\n\\n\\n\\nСообщение SAML неправильно отформатировано, содержит недостающие или недопустимые элементы
\\n\\n\\n\\nhttps://stackoverflow.com/questions/64158310/aws-sso-your-request-included-an-invalid-saml-response
\\n\\n\\n\\nИногда ошибка возникает не только с атрибутом User, но и в целом, если сообщение должно содержать всю необходимую информацию в требуемом формате. Например:
\\n\\n\\n\\nПомните, что SAML соответствует схеме, поэтому вы должны придерживаться его стандарта при создании XML-запроса. Обязательно обратитесь к этому документу, чтобы увидеть все стандартные теги.
\\n\\n\\n\\nЕще несколькими примерами возможных опечаток могут быть:
\\n\\n\\n\\nhttps://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_saml.html
\\n\\n\\n\\nВ этой статье есть несколько идей, которые помогут вам выявить точную причину проблемы. Тем не менее, я также хотел бы дать вам несколько основных советов по отладке полученного вами утверждения SAML, чтобы найти детали, которые могут указать вам на первопричину проблемы.
\\n\\n\\n\\n💡 Примечание по безопасности: поскольку утверждения SAML содержат конфиденциальную информацию, я не рекомендую вам использовать онлайн-декодеры base64 и использовать один из этих простых скриптов, чтобы сделать это с вашего локального терминала.
\\n\\n\\n\\nWindows systems (PowerShell):
\\n\\n\\n\\nPS C:\\\\\\\\>[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(\\\"base64encodedtext\\\"))
MacOS and Linux systems:
\\n\\n\\n\\n$echo \\\"base64encodedtext\\\" | base64 --decode
Кроме того, если атрибуты вашего поставщика идентификационных данных не зашифрованы, надстройка SAML Tracer для браузера Firefox или декодер SAML-сообщений Chrome могут просмотреть эти атрибуты.****.
\\n\\n\\n\\nЧтобы помочь вам в дальнейшем, вот две статьи из нашего блога, где мы делимся некоторыми советами по настройке SAML с GSuite (обратите внимание, что концепции и свойства аналогичны другим IdP).
\\n\\n\\n\\nВ этой статье мы рассмотрели, как устранить очень неприятную ошибку федерации SAML: “Response did not contain a valid SAML assertion”.
\\n\\n\\n\\nМы показали, что она может возникать, когда:
\\n\\n\\n\\nРолевые атрибуты не установлены правильно в SAML-запросе – На стороне IdP.
Имеет место десинхронизация времени между IdP и поставщиком услуг.
Имеется несоответствие Metadata.xml между субъектами, поэтому сертификат не совпадает.
В запросе есть опечатки или неверная структура SAML.
В общем, я всегда возвращаюсь к этой ссылке, когда мне нужно устранить неполадки в ответе SAML, поскольку проблема может заключаться в другой конфигурации в зависимости от используемого вами IdP.
\\n\\n\\n\\nЭта небольшая статья была полезна для всех вас, и до следующего раза, счастливых SAML утверждений, и до встречи в следующей статье! 😉.
\\n\\n\\n\\r\\nЕсли вы читаете эту статью, значит, вы управляете идентификационными данными пользователей за пределами AWS и используете Identity Provider (IdP) Federation для предоставления этим внешним идентификационным данным разрешения на использование ресурсов AWS в вашей учетной записи. В этом процессе аутентификации одной из наиболее распространенных ошибок, с которой вам, возможно, придется столкнуться, является “response did not contain […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Как закодить\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/kak-zakodit/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/kak-ispravit-otvet-ne-soderzhashhij-dejstvitelnogo-utverzhdeniya-saml/\"],\"title\":[0,\"Как исправить ответ, не содержащий действительного утверждения Saml - Gorlov.\"],\"breadcrumbTitle\":[0,\"Как исправить ответ, не содержащий действительного утверждения saml\"],\"description\":[0,\"Если вы читаете эту статью, значит, вы управляете идентификационными данными пользователей за пределами AWS и используете Identity Provider (IdP) Federation\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Если вы читаете эту статью, значит, вы управляете идентификационными данными пользователей за пределами AWS и используете Identity Provider (IdP) Federation\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Как исправить ответ, не содержащий действительного утверждения Saml - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T12:48:37+00:00\"],\"url\":[0,\"http://igorlov.loc/kak-ispravit-otvet-ne-soderzhashhij-dejstvitelnogo-utverzhdeniya-saml/\"]}]}],\"title\":[0,\"Как исправить ответ, не содержащий действительного утверждения saml\"],\"uri\":[0,\"/kak-ispravit-otvet-ne-soderzhashhij-dejstvitelnogo-utverzhdeniya-saml/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"kak-ispravit-otvet-ne-soderzhashhij-dejstvitelnogo-utverzhdeniya-saml\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Iam\\\"],\\\"id\\\":[0,\\\"dGVybToyMTA=\\\"],\\\"uri\\\":[0,\\\"/tag/iam/\\\"]}],[0,{\\\"name\\\":[0,\\\"Saml\\\"],\\\"id\\\":[0,\\\"dGVybToyMDk=\\\"],\\\"uri\\\":[0,\\\"/tag/saml/\\\"]}],[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"id\\\":[0,\\\"dGVybToyNg==\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMzA2\"],\"node\":[0,{\"date\":[0,\"2023-04-22T07:35:11\"],\"content\":[0,\"\\nИнтерактивность и непредсказуемость платформ для создания пользовательского контента (UGC) в прямом эфире во многом объясняет их популярность. Но эта непредсказуемость означает, что сообщества должны тщательно следить за своим контентом, чтобы убедиться, что он соответствует правилам сообщества или политике приемлемого использования и является подходящим, безопасным и доброжелательным для всех пользователей. Это часто приводит к созданию системы модерации, в которой пользователи сообщают о потенциальных нарушениях правил сообщества, а модераторы или администраторы принимают необходимые меры. Часто это ручной процесс, который оставляет желать лучшего. В последние годы инструменты искусственного интеллекта (ИИ) и машинного обучения (МЛ) усовершенствовались, и разработчики могут использовать их для помощи в модерировании своих сообществ. В этой статье мы рассмотрим один из способов сделать это с помощью Amazon Interactive Video Service (Amazon IVS) и Amazon Rekognition.
\\n\\n\\n\\nАнализ каждого кадра каждого живого потока в приложении с помощью AI/ML был бы очень дорогой и сложной задачей. Вместо этого разработчики могут анализировать образцы прямых трансляций в своих приложениях с определенной периодичностью, чтобы помочь модераторам, предупреждая их о наличии контента, нуждающегося в дальнейшем рассмотрении человеком-модератором. Это не 100% идеальное решение, но это один из способов автоматизировать модерацию контента и облегчить работу модераторов.
\\n\\n\\n\\nЭто решение включает в себя следующие шаги:
\\n\\n\\n\\nМы будем использовать AWS Serverless Application Model (SAM), чтобы упростить создание правила и функций. Вот весь файл template.yaml, который описывает необходимые разрешения, правило Amazon EventBridge, слой AWS Lambda (для зависимости от AWS SDK) и определения функций. Мы разберем это ниже.
\\n\\n\\n\\nAWSTemplateFormatVersion: '2010-09-09'\\nTransform: 'AWS::Serverless-2016-10-31'\\nDescription: Amazon IVS Moderation Functions\\nGlobals:\\n Function:\\n Runtime: nodejs18.x\\n Timeout: 30\\n MemorySize: 128\\n Api:\\n EndpointConfiguration: \\n Type: REGIONAL\\n Cors:\\n AllowMethods: \\\"'GET, POST, OPTIONS'\\\"\\n AllowHeaders: \\\"'Content-Type'\\\"\\n AllowOrigin: \\\"'*'\\\"\\n MaxAge: \\\"'600'\\\"\\nResources:\\n IvsChatLambdaRefLayer:\\n Type: AWS::Serverless::LayerVersion\\n Properties:\\n LayerName: sam-app-dependencies\\n Description: Dependencies for sam app\\n ContentUri: dependencies/\\n CompatibleRuntimes:\\n - nodejs18.x\\n LicenseInfo: \\\"MIT\\\"\\n RetentionPolicy: Retain\\n IVSAccessPolicy:\\n Type: AWS::IAM::Policy\\n Properties:\\n PolicyName: IVSModerationAccessPolicy\\n PolicyDocument:\\n Version: \\\"2012-10-17\\\"\\n Statement:\\n - Effect: Allow\\n Action:\\n - 's3:GetObject'\\n - 's3:GetObjectAcl'\\n - 'ivschat:SendEvent'\\n - 'ivs:StopStream'\\n - 'rekognition:DetectModerationLabels'\\n Resource: '*'\\n Roles:\\n - Ref: ModerateImageRole\\n - Ref: StopStreamRole\\n ApiAccessPolicy:\\n Type: AWS::IAM::Policy\\n Properties:\\n PolicyName: ApiAccessPolicy\\n PolicyDocument:\\n Version: \\\"2012-10-17\\\"\\n Statement:\\n - Effect: Allow\\n Action:\\n - 'sts:AssumeRole'\\n Resource: '*'\\n Roles:\\n - Ref: ModerateImageRole\\n - Ref: StopStreamRole\\n EventRule:\\n Type: AWS::Events::Rule\\n Properties:\\n Description: EventRule\\n State: ENABLED\\n EventPattern: \\n source:\\n - aws.s3\\n detail-type:\\n - \\\"Object Created\\\"\\n detail:\\n bucket:\\n name:\\n - ivs-demo-channel-stream-archive\\n object:\\n key:\\n - suffix: .jpg\\n Targets:\\n - Arn: !GetAtt ModerateImage.Arn\\n Id: MyLambdaFunctionTarget\\n PermissionForEventsToInvokeLambda:\\n Type: AWS::Lambda::Permission\\n Properties:\\n FunctionName: !Ref ModerateImage\\n Action: lambda:InvokeFunction\\n Principal: events.amazonaws.com\\n SourceArn: !GetAtt EventRule.Arn\\n ModerateImage:\\n Type: 'AWS::Serverless::Function'\\n Properties:\\n Environment:\\n Variables:\\n DEMO_CHAT_ARN: 'arn:aws:ivschat:us-east-1:[redacted]:room/[redacted]'\\n DEMO_CHANNEL_ARN: 'arn:aws:ivs:us-east-1:[redacted]:channel/[redacted]'\\n Handler: index.moderateImage\\n Layers:\\n - !Ref IvsChatLambdaRefLayer\\n CodeUri: lambda/\\n StopStream:\\n Type: 'AWS::Serverless::Function'\\n Properties:\\n Environment:\\n Variables:\\n DEMO_CHAT_ARN: 'arn:aws:ivschat:us-east-1:[redacted]:room/[redacted]'\\n DEMO_CHANNEL_ARN: 'arn:aws:ivs:us-east-1:[redacted]:channel/[redacted]'\\n Handler: index.stopStream\\n Layers:\\n - !Ref IvsChatLambdaRefLayer\\n CodeUri: lambda/\\n Events:\\n Api1:\\n Type: Api\\n Properties:\\n Path: /stop-stream\\n Method: POST\\nOutputs:\\n ApiURL:\\n Description: \\\"API endpoint URL for Prod environment\\\"\\n Value: !Sub \\\"https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/\\\"\\n
\\n\\n\\n\\nВ этом файле многое происходит, поэтому давайте немного разберем его. Во-первых, мы создаем слой для включения AWS SDK for JavaScript (v3) в нашу функцию.
\\n\\n\\n\\nIvsChatLambdaRefLayer:\\n Type: AWS::Serverless::LayerVersion\\n Properties:\\n LayerName: sam-app-dependencies\\n Description: Dependencies for sam app\\n ContentUri: dependencies/\\n CompatibleRuntimes:\\n - nodejs18.x\\n LicenseInfo: \\\"MIT\\\"\\n RetentionPolicy: Retain\\n
\\n\\n\\n\\nВ каталоге dependencies/nodejs находится файл package.json, который включает модули, необходимые нашей функции.
\\n\\n\\n\\n{\\n \\\"dependencies\\\": {\\n \\\"@aws-sdk/client-ivs\\\": \\\"^3.289.0\\\",\\n \\\"@aws-sdk/client-ivschat\\\": \\\"^3.289.0\\\",\\n \\\"@aws-sdk/client-rekognition\\\": \\\"^3.289.0\\\"\\n }\\n}\\n
\\n\\n\\n\\nСледующий раздел, обозначенный ключами IVSAccessPolicy и APIAccessPolicy, дает нашему бессерверному приложению возможность получить доступ к необходимым API (s3:GetObject, s3:GetObjectAcl, ivschat:SendEvent, ivs:StopStream и rekognition:DetectModerationLabels) и выставить метод остановки потока, который мы создадим ниже, через Amazon API Gateway.
\\n\\n\\n\\nДалее мы создаем правило Amazon EventBridge. Свойство name в поле bucket должно соответствовать имени ведра Amazon S3, которое вы настроили в конфигурации записи. Запись на Amazon S3 создает различные файлы, включая плейлисты и HLS-медиа, поэтому мы можем отфильтровать это правило, чтобы оно срабатывало только для наших миниатюр, установив ключ в объекте как suffix: jpg.
\\n\\n\\n\\nEventRule:\\n Type: AWS::Events::Rule\\n Properties:\\n Description: EventRule\\n State: ENABLED\\n EventPattern: \\n source:\\n - aws.s3\\n detail-type:\\n - \\\"Object Created\\\"\\n detail:\\n bucket:\\n name:\\n - ivs-demo-channel-stream-archive\\n object:\\n key:\\n - suffix: .jpg\\n Targets:\\n - Arn: !GetAtt ModerateImage.Arn\\n Id: MyLambdaFunctionTarget\\n
\\n\\n\\n\\nДалее мы предоставляем правилу необходимые разрешения для вызова функции AWS Lambda.
\\n\\n\\n\\nPermissionForEventsToInvokeLambda:\\n Type: AWS::Lambda::Permission\\n Properties:\\n FunctionName: !Ref ModerateImage\\n Action: lambda:InvokeFunction\\n Principal: events.amazonaws.com\\n SourceArn: !GetAtt EventRule.Arn\\n
\\n\\n\\n\\nТеперь мы можем определить нашу функцию, которая будет вызываться правилом Amazon EventBridge.
\\n\\n\\n\\nModerateImage:\\n Type: 'AWS::Serverless::Function'\\n Properties:\\n Environment:\\n Variables:\\n DEMO_CHAT_ARN: 'arn:aws:ivschat:us-east-1:[redacted]:room/[redacted]'\\n DEMO_CHANNEL_ARN: 'arn:aws:ivs:us-east-1:[redacted]:channel/[redacted]'\\n Handler: index.moderateImage\\n Layers:\\n - !Ref IvsChatLambdaRefLayer\\n CodeUri: lambda/\\n
\\n\\n\\n\\nПримечание: я объявляю DEMO_CHAT_ARN и DEMO_CHANNEL_ARN как переменные среды, но ваше приложение, скорее всего, будет получать значения ARN из события, передаваемого в функцию, поскольку вы, вероятно, будете использовать эту функциональность не только с одним каналом Amazon IVS.
\\n\\n\\n\\nНаконец, мы можем определить функцию, которая будет использоваться для остановки потока, если это необходимо.
\\n\\n\\n\\nStopStream:\\n Type: 'AWS::Serverless::Function'\\n Properties:\\n Environment:\\n Variables:\\n DEMO_CHAT_ARN: 'arn:aws:ivschat:us-east-1:[redacted]:room/[redacted]'\\n DEMO_CHANNEL_ARN: 'arn:aws:ivs:us-east-1:[redacted]:channel/[redacted]'\\n Handler: index.stopStream\\n Layers:\\n - !Ref IvsChatLambdaRefLayer\\n CodeUri: lambda/\\n Events:\\n Api1:\\n Type: Api\\n Properties:\\n Path: /stop-stream\\n Method: POST\\n
\\n\\n\\n\\nТеперь, когда мы описали нашу инфраструктуру с помощью AWS SAM, давайте создадим описанные нами функции. В файле index.mjs мы импортируем классы SDK, получим значения Arn из переменных окружения, которые мы передали, и создадим экземпляры клиентов, необходимых для наших функций.
\\n\\n\\n\\nimport { IvsClient, StopStreamCommand } from \\\"@aws-sdk/client-ivs\\\";\\nimport { IvschatClient, SendEventCommand } from \\\"@aws-sdk/client-ivschat\\\";\\nimport { RekognitionClient, DetectModerationLabelsCommand } from \\\"@aws-sdk/client-rekognition\\\";\\n\\nconst chatArn = process.env.DEMO_CHAT_ARN;\\nconst channelArn = process.env.DEMO_CHANNEL_ARN;\\n\\nconst ivsClient = new IvsClient();\\nconst ivsChatClient = new IvschatClient();\\nconst rekognitionClient = new RekognitionClient();\\n
\\n\\n\\n\\nФункция moderateImage получит событие Amazon EventBridge, извлечет bucket и ключ из события и отправит команду DetectModerationLabelsCommand через rekognitionClient для обнаружения любого неуместного или оскорбительного содержимого в изображениях на основе перечисленных здесь категорий.
\\n\\n\\n\\nexport const moderateImage = async (event) => {\\n console.log('moderateImage:', JSON.stringify(event, null, 2));\\n const bucket = event.detail.bucket.name;\\n const key = event.detail.object.key;\\n\\n const detectLabelsCommandInput = {\\n Image: {\\n S3Object: {\\n Bucket: bucket,\\n Name: key,\\n }\\n },\\n };\\n const detectLabelsRequest = new DetectModerationLabelsCommand(detectLabelsCommandInput);\\n const detectLabelsResponse = await rekognitionClient.send(detectLabelsRequest);\\n\\n if (detectLabelsResponse.ModerationLabels) {\\n sendEvent('STREAM_MODERATION', detectLabelsResponse.ModerationLabels);\\n }\\n};\\n
\\n\\n\\n\\nПри необходимости функция moderateImage вызывает sendEvent для публикации пользовательского события для всех фронт-эндов, подключенных к данной чат-комнате Amazon IVS.
\\n\\n\\n\\nconst sendEvent = async (eventName, eventDetails) => {\\n const sendEventInput = {\\n roomIdentifier: chatArn,\\n attributes: {\\n streamModerationEvent: JSON.stringify(eventDetails),\\n },\\n eventName,\\n };\\n const sendEventRequest = new SendEventCommand(sendEventInput);\\n await ivsChatClient.send(sendEventRequest);\\n};\\n
\\n\\n\\n\\nВаш фронт-энд может решить, как обрабатывать это событие, а логика публикации этого события будет зависеть от ваших бизнес-потребностей. Может быть, вы предпочтете запустить пользовательский сигнал тревоги в CloudWatch, отправить электронное письмо или опубликовать уведомление через Amazon SNS? Потребности каждого приложения отличаются, но данные о модерации доступны на данном этапе, чтобы делать с ними то, что вам нужно.
\\n\\n\\n\\nМетод stopStream использует ivsClient для отправки команды StopStreamCommand. Опять же, реализация этого зависит от вас. Вы даже можете полностью автоматизировать эту команду, если результат Amazon Rekognition соответствует определенной категории или превышает уровень доверия.
\\n\\n\\n\\nexport const stopStream = async (event) => {\\n console.log('stopStream:', JSON.stringify(event, null, 2));\\n try {\\n const stopStreamRequest = new StopStreamCommand({ channelArn });\\n const stopStreamResponse = await ivsClient.send(stopStreamRequest);\\n responseObject.body = JSON.stringify(stopStreamResponse);\\n }\\n catch (err) {\\n responseObject.statusCode = err?.name === 'ChannelNotBroadcasting' ? 404 : 500;\\n responseObject.body = JSON.stringify(err);\\n }\\n return responseObject;\\n};\\n
\\n\\n\\n\\nВ своей демонстрации я решил прослушивать пользовательские события и отображать результаты в представлении модератора, которое показывает обнаруженный элемент и уровень доверия. Я также предлагаю модератору кнопку “Остановить поток”, которая вызывает метод stopStream через открытый Amazon API Gateway.
\\n\\n\\n\\nВ этой статье мы узнали, как использовать Amazon Rekognition для помощи модераторам в модерировании контента в приложениях, которые они создают с помощью Amazon IVS. Если вы хотите узнать больше о том, как Amazon IVS может помочь создать более безопасные сообщества пользовательского контента, ознакомьтесь со следующими статьями блога:
\\n\\n\\n\\r\\nИнтерактивность и непредсказуемость платформ для создания пользовательского контента (UGC) в прямом эфире во многом объясняет их популярность. Но эта непредсказуемость означает, что сообщества должны тщательно следить за своим контентом, чтобы убедиться, что он соответствует правилам сообщества или политике приемлемого использования и является подходящим, безопасным и доброжелательным для всех пользователей. Это часто приводит к созданию системы […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Как закодить\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/kak-zakodit/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/sozdanie-bolee-bezopasnyh-onlajn-soobshhestv-s-pomoshhyu-ii-ml-moderaczii-kontenta/\"],\"title\":[0,\"Создание более безопасных онлайн-сообществ с помощью ИИ/МЛ модерации контента - Gorlov.\"],\"breadcrumbTitle\":[0,\"Создание более безопасных онлайн-сообществ с помощью ИИ/МЛ модерации контента\"],\"description\":[0,\"Интерактивность и непредсказуемость платформ для создания пользовательского контента (UGC) в прямом эфире во многом объясняет их популярность. Но эта\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Интерактивность и непредсказуемость платформ для создания пользовательского контента (UGC) в прямом эфире во многом объясняет их популярность. Но эта\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Создание более безопасных онлайн-сообществ с помощью ИИ/МЛ модерации контента - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T13:09:41+00:00\"],\"url\":[0,\"http://igorlov.loc/sozdanie-bolee-bezopasnyh-onlajn-soobshhestv-s-pomoshhyu-ii-ml-moderaczii-kontenta/\"]}]}],\"title\":[0,\"Создание более безопасных онлайн-сообществ с помощью ИИ/МЛ модерации контента\"],\"uri\":[0,\"/sozdanie-bolee-bezopasnyh-onlajn-soobshhestv-s-pomoshhyu-ii-ml-moderaczii-kontenta/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"sozdanie-bolee-bezopasnyh-onlajn-soobshhestv-s-pomoshhyu-ii-ml-moderaczii-kontenta\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Amazon Rekognition\\\"],\\\"id\\\":[0,\\\"dGVybToyMDg=\\\"],\\\"uri\\\":[0,\\\"/tag/amazon-rekognition/\\\"]}],[0,{\\\"name\\\":[0,\\\"AWS\\\"],\\\"id\\\":[0,\\\"dGVybToxNTI=\\\"],\\\"uri\\\":[0,\\\"/tag/aws/\\\"]}],[0,{\\\"name\\\":[0,\\\"Как закодить\\\"],\\\"id\\\":[0,\\\"dGVybToyNg==\\\"],\\\"uri\\\":[0,\\\"/category/kak-zakodit/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMzAz\"],\"node\":[0,{\"date\":[0,\"2023-04-22T07:12:18\"],\"content\":[0,\"\\nВ этом руководстве я расскажу вам, как создать пользовательский валидатор данных в PHP, шаг за шагом создавая нашу собственную библиотеку валидации. Валидаторы данных являются важными инструментами для любого разработчика, которому необходимо обеспечить достоверность и безопасность данных, передаваемых пользователем. К концу этого урока вы будете хорошо понимать, как создавать пользовательские валидаторы данных в PHP, что позволит вам лучше обрабатывать пользовательские данные и обеспечивать безопасность ваших приложений.
\\n\\n\\n\\nПервым шагом является создание класса, который будет обрабатывать валидацию. Этот класс должен быть способен хранить правила проверки для каждого поля, которое мы хотим проверить, а также проверять эти правила при вызове.
\\n\\n\\n\\nВот пример простого класса для проверки:
\\n\\n\\n\\n<?php\\n\\nnamespace DevCoder\\\\Validator;\\n\\nuse DevCoder\\\\Validator\\\\Assert\\\\ValidatorInterface;\\nuse InvalidArgumentException;\\nuse function get_class;\\nuse function gettype;\\nuse function is_array;\\nuse function is_object;\\nuse function sprintf;\\n\\nclass Validation\\n{\\n /**\\n * @var array<string,array>\\n */\\n private $validators;\\n\\n /**\\n * @var array<string,string>\\n */\\n private $errors = [];\\n\\n /**\\n * @var array\\n */\\n private $data = [];\\n\\n public function __construct(array $fieldValidators)\\n {\\n foreach ($fieldValidators as $field => $validators) {\\n if (!is_array($validators)) {\\n $validators = [$validators];\\n }\\n $this->addValidator($field, $validators);\\n }\\n }\\n\\n public function validate(array $data): bool\\n {\\n $this->data = $data;\\n\\n /**\\n * @var $validators array<ValidatorInterface>\\n */\\n foreach ($this->validators as $field => $validators) {\\n if (!isset($this->data[$field])) {\\n $this->data[$field] = null;\\n }\\n\\n foreach ($validators as $validator) {\\n if ($validator->validate($this->data[$field]) === false) {\\n $this->addError($field, (string)$validator->getError());\\n }\\n }\\n\\n }\\n return $this->getErrors() === [];\\n }\\n\\n /**\\n * @return array<string,string>\\n */\\n public function getErrors(): array\\n {\\n return $this->errors;\\n }\\n\\n /**\\n * @return array\\n */\\n public function getData(): array\\n {\\n return $this->data;\\n }\\n\\n private function addError(string $field, string $message): void\\n {\\n $this->errors[$field][] = $message;\\n }\\n\\n /**\\n * @param string $field\\n * @param array<ValidatorInterface> $validators\\n * @return void\\n */\\n private function addValidator(string $field, array $validators): void\\n {\\n foreach ($validators as $validator) {\\n if (!$validator instanceof ValidatorInterface) {\\n throw new InvalidArgumentException(sprintf(\\n $field . ' validator must be an instance of ValidatorInterface, \\\"%s\\\" given.',\\n is_object($validator) ? get_class($validator) : gettype($validator)\\n ));\\n }\\n\\n $this->validators[$field][] = $validator;\\n }\\n }\\n}\\n\\n
\\n\\n\\n\\nТеперь, когда мы создали класс Validator, следующим шагом будет создание собственных правил валидации. Эти правила будут использоваться для проверки того, являются ли предоставленные данные действительными или нет. Мы создадим их в отдельных файлах, по одному для каждого правила валидации. Каждый файл правила валидации должен содержать класс, названный в честь правила, которое он реализует. Например, если у нас есть правило валидации, проверяющее, является ли значение целым числом, мы назовем класс Integer.
\\n\\n\\n\\n<?php\\n\\nnamespace DevCoder\\\\Validator\\\\Assert;\\n\\ninterface ValidatorInterface\\n{\\n public function validate($value): bool;\\n public function getError(): ?string;\\n}\\n
\\n\\n\\n\\n<?php\\n\\nnamespace DevCoder\\\\Validator\\\\Assert;\\n\\nabstract class AbstractValidator implements ValidatorInterface\\n{\\n /**\\n * @var string|null\\n */\\n protected $error;\\n\\n public function getError(): ?string\\n {\\n return $this->error;\\n }\\n\\n protected function error(string $message, array $context): void\\n {\\n $replace = [];\\n foreach ($context as $key => $value) {\\n if (is_object($value)) {\\n $value = method_exists($value, '__toString') ? (string)$value : get_class($value);\\n } elseif (is_array($value)) {\\n $value = json_encode($value);\\n } else {\\n $value = (string)$value;\\n }\\n $replace['{{ ' . $key . ' }}'] = $value;\\n }\\n\\n $this->error = strtr($message, $replace);\\n }\\n}\\n
\\n\\n\\n\\n<?php\\n\\ndeclare(strict_types=1);\\n\\nnamespace DevCoder\\\\Validator\\\\Assert;\\n\\nuse function ctype_digit;\\nuse function is_int;\\nuse function strval;\\n\\nclass Integer extends AbstractValidator\\n{\\n /**\\n * @var string\\n */\\n private $invalidMessage = 'This value should be of type {{ type }}.';\\n private $minMessage = '{{ value }} should be {{ limit }} or more.';\\n private $maxMessage = '{{ value }} should be {{ limit }} or less.';\\n\\n /**\\n * @var int|null\\n */\\n private $min;\\n /**\\n * @var int|null\\n */\\n private $max;\\n\\n public function validate($value): bool\\n {\\n if ($value === null) {\\n return true;\\n }\\n\\n if (ctype_digit(strval($value)) === false) {\\n $this->error($this->invalidMessage, ['value' => $value, 'type' => 'integer']);\\n return false;\\n }\\n\\n if (is_int($this->min) && $value < $this->min) {\\n $this->error($this->minMessage, ['value' => $value, 'limit' => $this->min]);\\n return false;\\n }\\n\\n if (is_int($this->max) && $value > $this->max) {\\n $this->error($this->maxMessage, ['value' => $value, 'limit' => $this->max]);\\n return false;\\n }\\n\\n return true;\\n }\\n\\n public function invalidMessage(string $invalidMessage): self\\n {\\n $this->invalidMessage = $invalidMessage;\\n return $this;\\n }\\n\\n public function minMessage(string $minMessage): self\\n {\\n $this->minMessage = $minMessage;\\n return $this;\\n }\\n\\n public function maxMessage(string $maxMessage): self\\n {\\n $this->maxMessage = $maxMessage;\\n return $this;\\n }\\n\\n public function min(int $min): self\\n {\\n $this->min = $min;\\n return $this;\\n }\\n\\n public function max(int $max): self\\n {\\n $this->max = $max;\\n return $this;\\n }\\n}\\n\\n
\\n\\n\\n\\n<?php\\n\\ndeclare(strict_types=1);\\n\\nnamespace DevCoder\\\\Validator\\\\Assert;\\n\\nclass NotNull extends AbstractValidator\\n{\\n private $message = 'This value should not be null.';\\n\\n public function validate($value): bool\\n {\\n if ($value === null) {\\n $this->error($this->message, ['value' => $value]);\\n return false;\\n }\\n\\n return true;\\n }\\n\\n public function message(string $message): self\\n {\\n $this->message = $message;\\n return $this;\\n }\\n}\\n\\n
\\n\\n\\n\\nЭтот объект принимает массив вариантов валидации в качестве входных данных. Ключами массива являются имена полей, а значениями — массивы валидаторов.
\\n\\n\\n\\n<?php\\n$validation = new Validator([\\n 'age' => [(new Integer())->min(18)->max(99), new NotNull()],\\n 'number_of_children' => [new NotNull(), new Integer()],\\n 'salary' => [new NotNull(), new Integer()],\\n]);\\n
\\n\\n\\n\\nПосле создания экземпляра класса Validation вы можете проверить данные, вызвав метод validate() класса Validation. Этот метод вернет true, если все правила валидации выполнены, и false в противном случае.
\\n\\n\\n\\n<?php\\n\\nif ($validation->validate($_POST) === true) {\\n $data = $validation->getData();\\n // save in database\\n // redirect in another page\\n}\\n\\nreturn render('template.html.php', [\\n 'errors' => $validation->getErrors()\\n]);\\n
\\n\\n\\n\\nПример других правил, которые могут быть добавлены
\\n\\n\\n\\n$validation = new Validation([\\n 'email' => [new NotNull(), new Email()],\\n 'password' => new NotNull(),\\n 'firstname' => [new NotNull(), (new StringLength())->min(3), new Alphabetic()],\\n 'lastname' => [(new StringLength())->min(3)],\\n 'gender' => new Choice(['Mme', 'Mr', null]),\\n 'website' => [new NotNull(), new Url()],\\n 'age' => [new NotNull(), (new Integer())->min(18)],\\n 'invoice_total' => [new NotNull(), new Numeric()],\\n 'active' => [new NotNull(), new Custom(function ($value) {\\n return is_bool($value);\\n })]\\n]);\\n
\\n\\n\\n\\nЧтобы увидеть другие правила валидации, которые можно добавить, посмотрите мой репозиторий на GitHub по следующему URL: https://github.com/devcoder-xyz/php-validator/tree/main/src/Assert.
\\n\\n\\n\\nИдеально подходит для небольшого проекта
Просто и легко!
https://github.com/devcoder-xyz/php-validator
В этом руководстве я расскажу вам, как создать пользовательский валидатор данных в PHP, шаг за шагом создавая нашу собственную библиотеку валидации. Валидаторы данных являются важными инструментами для любого разработчика, которому необходимо обеспечить достоверность и безопасность данных, передаваемых пользователем. К концу этого урока вы будете хорошо понимать, как создавать пользовательские валидаторы данных в PHP, что позволит […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/php-sozdanie-sobstvennogo-validatora-dannyh-na-php-shag-za-shagom/\"],\"title\":[0,\"PHP – Создание собственного валидатора данных на PHP: Шаг за шагом - Gorlov.\"],\"breadcrumbTitle\":[0,\"PHP – Создание собственного валидатора данных на PHP: Шаг за шагом\"],\"description\":[0,\"В этом руководстве я расскажу вам, как создать пользовательский валидатор данных в PHP, шаг за шагом создавая нашу собственную библиотеку валидации.\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"В этом руководстве я расскажу вам, как создать пользовательский валидатор данных в PHP, шаг за шагом создавая нашу собственную библиотеку валидации.\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"PHP – Создание собственного валидатора данных на PHP: Шаг за шагом - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T13:05:15+00:00\"],\"url\":[0,\"http://igorlov.loc/php-sozdanie-sobstvennogo-validatora-dannyh-na-php-shag-za-shagom/\"]}]}],\"title\":[0,\"PHP – Создание собственного валидатора данных на PHP: Шаг за шагом\"],\"uri\":[0,\"/php-sozdanie-sobstvennogo-validatora-dannyh-na-php-shag-za-shagom/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"php-sozdanie-sobstvennogo-validatora-dannyh-na-php-shag-za-shagom\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Php\\\"],\\\"id\\\":[0,\\\"dGVybToxMzg=\\\"],\\\"uri\\\":[0,\\\"/tag/php/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMzAy\"],\"node\":[0,{\"date\":[0,\"2023-04-22T07:00:29\"],\"content\":[0,\"\\nЗдравствуйте, разработчики,
\\n\\n\\n\\nВ этом блоге мы расскажем, как использовать DataDog для определения загрузки диска. Но перед этим расскажите мне, зачем это нужно.
\\n\\n\\n\\nПри использовании машин EC2 у нас обычно есть подключенный том EBS или дополнительный том EBS. Вы могли много раз видеть ошибку “No space left on device” или иногда 100% utilisation state подобного рода. При возникновении этой ошибки вы могли принять меры, добавив дополнительный том или изменив размер текущего тома EBS. Но иногда эти действия могут быть предприняты с опозданием, потому что мы узнаем об этом очень поздно, и это может привести к ухудшению пользовательского опыта. Иногда вы превышаете размер тома и в итоге платите дополнительные расходы. Как этого избежать?
\\n\\n\\n\\nЗдесь я объясню, как избежать этого и контролировать использование диска тома EBS с помощью Datadog. Как это может уберечь вас от любых инцидентов и сэкономить ваши расходы.
\\n\\n\\n\\nДавайте сначала разберемся, почему использование диска имеет большое значение.
\\n\\n\\n\\nВаш диск – это не что иное, как жесткий диск или том облачного экземпляра. Он используется не только для сохранения данных или кода, но и играет важную роль в операциях записи и чтения. Он имеет такой же вес, как процессор или оперативная память. Каждый жесткий диск способен выполнять множество операций чтения и записи, которые определяют скорость IOPS. Это приведет к замедлению работы вашего экземпляра или ПК. Клиенты или конечные пользователи могут столкнуться с проблемами при доступе к приложениям. Использование диска может достичь предела в 90% или 100% только потому, что код или приложение требует больше операций чтения и записи, которые вы не можете сократить.
\\n\\n\\n\\nПоэтому метрики использования диска важны для потребителей, чтобы знать, сколько хранилища используется недостаточно или чрезмерно, чтобы оптимизировать затраты на диск. А также преодолеть любой инцидент. Наблюдая за метриками использования диска, можно выяснить, какая модель использования ожидается в общем приложении, и соответственно изменить размер диска.
\\n\\n\\n\\nНо вопрос в том, как я могу получить раннее уведомление, когда использование диска достигает некоторого порога? Как я могу узнать, каково среднее использование диска? Здесь может помочь Datadog.
\\n\\n\\n\\nDatadog – одна из лучших платформ для мониторинга и управления журналами. Вы можете интегрировать свои локальные или облачные экземпляры, или сервисы, чтобы получить все журналы и метрики в одном месте. Datadog обеспечивает мониторинг в режиме реального времени и инициирование событий на основе пороговых значений метрик или сообщений журналов для принятия соответствующих мер. Источник изображения — официальный сайт Datadog
\\n\\n\\n\\nDatadog предоставляет агенты установки, которые должны быть установлены на сервере или экземпляре. Этот агент будет продолжать сбрасывать данные регистрации и метрики на приборную панель Datadog. Datadog имеет механизм сборки на основе пороговых показателей, например, когда “использование процессора более 80%” или “использование диска более 70%” может вызвать уведомление соответствующей команды или члена команды по электронной почте или Slack или ops genie и т.д. Также на основе модели использования вы можете настроить масштабирование диска.
\\n\\n\\n\\nДавайте рассмотрим на примере тома AWS EBS. AWS EBS volume предоставляет метрики CloudWatch, которые легко интегрируются с Datadog. Также, как было сказано выше, можно установить агент и получать журналы в течение 15 секунд.
\\n\\n\\n\\nДавайте разберемся, как DataDog может быть интегрирован с томом AWS EBS и получать необходимые метрики. Для этого необходимо, чтобы у вас уже был запущен AWS EC2 с подключенным томом по умолчанию.
\\n\\n\\n\\n.
\\n\\n\\n\\nНа этом интеграция Datadog с экземпляром завершена, и мы можем просматривать метрики объема EBS и отслеживать закономерности для принятия решения об оптимизации затрат 🙂
\\n\\n\\n\\nНадеюсь, этот блог поможет вам в обучении. Не стесняйтесь обращаться ко мне в Twitter @AvinashDalvi_ или оставлять комментарии в блоге.
\\n\\n\\n\\r\\nЗдравствуйте, разработчики, В этом блоге мы расскажем, как использовать DataDog для определения загрузки диска. Но перед этим расскажите мне, зачем это нужно. При использовании машин EC2 у нас обычно есть подключенный том EBS или дополнительный том EBS. Вы могли много раз видеть ошибку “No space left on device” или иногда 100% utilisation state подобного рода. […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/how-to-use-datadog-to-find-utilisation-of-aws-ebs-volume/\"],\"title\":[0,\"Как использовать DataDog для определения использования тома AWS EBS - Gorlov.\"],\"breadcrumbTitle\":[0,\"Как использовать DataDog для определения использования тома AWS EBS\"],\"description\":[0,\"Здравствуйте, разработчики,\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Здравствуйте, разработчики,\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Как использовать DataDog для определения использования тома AWS EBS - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T13:05:21+00:00\"],\"url\":[0,\"http://igorlov.loc/how-to-use-datadog-to-find-utilisation-of-aws-ebs-volume/\"]}]}],\"title\":[0,\"Как использовать DataDog для определения использования тома AWS EBS\"],\"uri\":[0,\"/how-to-use-datadog-to-find-utilisation-of-aws-ebs-volume/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"how-to-use-datadog-to-find-utilisation-of-aws-ebs-volume\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"AWS\\\"],\\\"id\\\":[0,\\\"dGVybToxNTI=\\\"],\\\"uri\\\":[0,\\\"/tag/aws/\\\"]}],[0,{\\\"name\\\":[0,\\\"Datadog\\\"],\\\"id\\\":[0,\\\"dGVybToyMDc=\\\"],\\\"uri\\\":[0,\\\"/tag/datadog/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMjU4\"],\"node\":[0,{\"date\":[0,\"2023-04-21T07:41:33\"],\"content\":[0,\"\\nС появлением генераторов статических сайтов (SSG), таких как Eleventy, создание стильного и эффективного статического сайта стало как никогда простым.
\\n\\n\\n\\nВ этой статье мы рассмотрим, как использовать Eleventy для создания потрясающего и функционального статического сайта портфолио без использования серверного языка или базы данных.
\\n\\n\\n\\nВы также узнаете, как развернуть статический сайт прямо из репозитория GitHub на платформе хостинга приложений Kinsta, чтобы быстро запустить сайт на бесплатном домене .kinsta.app.
\\n\\n\\n\\nВот демонстрация статического сайта портфолио, который вы создадите с помощью Eleventy.
\\n\\n\\n\\nСоздание стильного статического веб-сайта с Eleventy (11ty)
Вы можете получить доступ к репозиторию GitHub этого проекта, если хотите взглянуть на него поближе.
\\n\\n\\n\\nEleventy, также известный как 11ty, является генератором статических сайтов, который создает веб-сайты на основе HTML, CSS и JavaScript без необходимости использования баз данных и внутренних языков программирования.
\\n\\n\\n\\nEleventy известен своей простотой и гибкостью, поскольку он не заставляет вас использовать только один язык шаблонов или фреймворк. Он поддерживает более 10 языков шаблонов и даже позволяет использовать столько языков, сколько вы хотите, в одном проекте:
\\n\\n\\n\\nОдиннадцать языков шаблонов
Eleventy, как и большинство SSG, позволяет создавать содержимое статического сайта с помощью многократно используемых компонентов, а не создавать полные HTML-документы для каждой страницы.
\\n\\n\\n\\nПримечание: Когда вы выполните команду npx @11ty/eleventy. Вы получите следующее сообщение:
\\n\\n\\n\\n[11ty] Wrote 0 files in 0.01 seconds (v2.0.0)
\\n\\n\\n\\nЗдесь записывается 0 файлов, потому что в папке вашего проекта нет шаблонов.
\\n\\n\\n\\nТеперь вы создали свой проект Eleventy, но это еще не все. Вам нужно создать некоторые конфигурации и знать некоторые основные команды для вашего статического сайта, который может быть передан браузеру.
\\n\\n\\n\\nВот некоторые ключевые команды Eleventy, которые вы должны знать:
\\n\\n\\n\\nВам не нужно запоминать эти команды, потому что вы можете добавить их к общим командам в объект scripts вашего файла package.json:
\\n\\n\\n\\n\\\"scripts\\\": {\\n \\\"start\\\": \\\"npx @11ty/eleventy --serve\\\",\\n \\\"watch\\\": \\\"npx @11ty/eleventy --watch\\\",\\n \\\"build\\\": \\\"npx eleventy\\\"\\n },
\\n\\n\\n\\nТеперь вы можете использовать npm start для обслуживания вашего приложения вместо npx @11ty/eleventy –serve, а также запустить npm run build вместо npx eleventy.
\\n\\n\\n\\nПо умолчанию Eleventy имеет “нулевую конфигурацию” и гибкие возможности настройки. Вот некоторые ключевые параметры конфигурации, которые вы должны знать:
\\n\\n\\n\\nЭто лишь несколько команд и опций конфигурации, доступных в Eleventy. Чтобы настроить проект Eleventy, создайте файл .eleventy.js в корне вашего проекта. Затем вставьте этот код в файл, чтобы придать проекту структуру, включающую каталоги ввода и вывода:
\\n\\n\\n\\nmodule.exports = function (eleventyConfig) {\\n return {\\n dir: {\\n input: 'src',\\n output: 'public',\\n },\\n };\\n};
\\n\\n\\n\\nПримечание: eleventyConfig передается в качестве аргумента, предоставляя больше опций конфигурации, которые будут использованы позже в этом проекте.
\\n\\n\\n\\nТеперь вы знаете некоторые ключевые команды, которые можно использовать для предварительного просмотра статического сайта Eleventy, но когда вы выполняете команду, например, npx @11ty/eleventy, ничего не отображается. Это происходит потому, что у вас нет файла шаблона.
\\n\\n\\n\\nВы можете создать папку src в корневой папке вашего проекта, затем создать несколько файлов шаблонов, таких как index.html, или использовать предпочитаемый вами язык шаблонов для представления домашней страницы:
\\n\\n\\n\\n<!DOCTYPE html>\\n<html lang=\\\"en\\\">\\n <head>\\n <meta charset=\\\"UTF-8\\\" />\\n <meta http-equiv=\\\"X-UA-Compatible\\\" content=\\\"IE=edge\\\" />\\n <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1.0\\\" />\\n <title>Eleventy Static Site</title>\\n </head>\\n <body>\\n Hello World!\\n </body>\\n</html>
\\n\\n\\n\\nЕсли теперь вы выполните команду npx @11ty/eleventy, будет создана общая папка со сгенерированным статическим файлом. Вы обязательно захотите, чтобы он был передан в ваш браузер и включил некоторые функции горячей перезагрузки. Это можно сделать, выполнив следующую команду:
\\n\\n\\n\\nnpx @11ty/eleventy --serve
\\n\\n\\n\\nЭто позволит обслуживать ваш сайт на http://localhost:8080/.
\\n\\n\\n\\nЭти команды довольно сложно запомнить и постоянно использовать. Вы уже добавили их в привычный синтаксис в файле package.json, поэтому вы можете использовать npm start для обслуживания вашего приложения на http://localhost:8080/.
\\n\\n\\n\\nТеперь вы знаете, как создать статический сайт с помощью Eleventy. Давайте создадим проект портфолио.
\\n\\n\\n\\nВы можете создать новый проект Eleventy с нуля, или вам понадобятся изображения, CSS и фактический контент для вашего проекта, поэтому мы создали шаблон репозитория GitHub, чтобы помочь вам ускорить процесс. В GitHub выберите Use this template > Create a new repository, чтобы скопировать эти активы и файлы начальной конфигурации в новый собственный репозиторий, а затем загрузите их на локальную машину.
\\n\\n\\n\\nВаш проект будет иметь следующую структуру:
\\n\\n\\n\\n├── node_modules/\\n├── public/\\n├── src/\\n | ├── _includes\\n | ├── layouts\\n │ ├── assets\\n │ ├── css\\n │ ├── projects\\n │ └── index.njk\\n├── .eleventy.js\\n├── .gitignore\\n├── package.lock.json\\n└── package.json
\\n\\n\\n\\nПри использовании Eleventy есть три основных типа шаблонов, которые вам необходимо понять. Эти шаблоны могут быть созданы с помощью Nunjucks, который позволяет вам определять переменные, циклы, условия и другую логику, которая может быть использована для динамической генерации содержимого страницы.
\\n\\n\\n\\nТеперь, когда вы понимаете каждый из этих типов шаблонов. Давайте создадим шаблоны для статического сайта-портфолио.
\\n\\n\\n\\nВнутри каталога src создайте каталог _includes. В ней будут содержаться все наши макеты и части.
\\n\\n\\n\\nЗатем вы можете создать папку layouts (для правильной организации), в которой будут храниться все ваши макеты. Эти макеты являются шаблонами и могут использовать предпочитаемый вами язык шаблонов, например, Nunjucks, который мы используем здесь.
\\n\\n\\n\\nДавайте создадим файл base.njk для хранения общего макета для всех ваших страниц.
\\n\\n\\n\\n<!DOCTYPE html>\\n<html lang=\\\"en\\\">\\n <head>\\n <meta charset=\\\"UTF-8\\\" />\\n <meta http-equiv=\\\"X-UA-Compatible\\\" content=\\\"IE=edge\\\" />\\n <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1.0\\\" />\\n <link rel=\\\"icon\\\" href=\\\"https://kinsta.com/assets/favicon.jpeg\\\" />\\n <link\\n rel=\\\"stylesheet\\\"\\n href=\\\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css\\\"\\n integrity=\\\"sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==\\\"\\n crossorigin=\\\"anonymous\\\"\\n referrerpolicy=\\\"no-referrer\\\"\\n />\\n <link rel=\\\"stylesheet\\\" href=\\\"http://kinsta.com/css/global.css\\\" />\\n <title>J.'s Portfolio</title>\\n </head>\\n <body>\\n <div>\\n {{ content | safe }}\\n </div>\\n </body>\\n</html>
\\n\\n\\n\\nВ приведенном выше коде создается общая HTML-разметка и включается Font Awesome из CDN, чтобы вы могли получить доступ к его иконкам. Также передается переменная content, чтобы все содержимое любой страницы, использующей этот макет, было включено.
\\n\\n\\n\\nНо это еще не вся история макета. В вашем макете будут некоторые разделы, которые будут появляться на каждой странице, например, панель навигации и нижний колонтитул. Давайте создадим партиции для каждого из этих разделов.
\\n\\n\\n\\nВсе части хранятся в каталоге _includes. Для правильной организации вы можете хранить их в папке. В этом случае создайте папку components в каталоге _includes и создайте шаблоны navbar и footer.
\\n\\n\\n\\nВот партиклы Navbar в файле navbar.njk:
\\n\\n\\n\\n<div class=\\\"nav-container\\\">\\n <div class=\\\"logo\\\">\\n <a href=\\\"https://kinsta.com/\\\">\\n J.\\n </a>\\n </div>\\n <div class=\\\"nav\\\">\\n <a href=\\\"http://kinsta.com/projects\\\" class=\\\"link\\\">\\n Projects\\n </a>\\n <a href=\\\"https://docs.google.com/document/d/10ZosQ38Z3804KYPcb_aZp9bceoXK-q3GrkHjYshqIRE/edit?usp=sharing\\\" class=\\\"cta-btn\\\">Resume</a>\\n </div>\\n</div>
\\n\\n\\n\\nВот партиклы футера в файле footer.njk:
\\n\\n\\n\\n<hr />\\n<div class=\\\"footer-container\\\">\\n <p>© {% year %} Joel's Portfolio</p>\\n <div class=\\\"social_icons\\\">\\n <a\\n href=\\\"https://twitter.com/olawanle_joel\\\"\\n aria-label=\\\"Twitter\\\"\\n target=\\\"_blank\\\"\\n rel=\\\"noopener noreferrer\\\"\\n >\\n <i class=\\\"fa-brands fa-twitter\\\"></i>\\n </a>\\n <a\\n href=\\\"https://github.com/olawanlejoel\\\"\\n aria-label=\\\"GitHub\\\"\\n target=\\\"_blank\\\"\\n rel=\\\"noopener noreferrer\\\"\\n >\\n <i class=\\\"fa-brands fa-github\\\"></i>\\n </a>\\n <a\\n href=\\\"https://www.linkedin.com/in/olawanlejoel/\\\"\\n aria-label=\\\"LinkedIn\\\"\\n target=\\\"_blank\\\"\\n rel=\\\"noopener noreferrer\\\"\\n >\\n <i class=\\\"fa-brands fa-linkedin\\\"></i>\\n </a>\\n </div>\\n</div>
\\n\\n\\n\\nДобавьте эти части в шаблон страницы или макета. Это можно сделать с помощью оператора {% include %}. Вот как будет выглядеть шаблон layouts/base.njk, если включить в него шаблоны navbar и footer:
\\n\\n\\n\\n<!DOCTYPE html>\\n<html lang=\\\"en\\\">\\n <head>\\n <meta charset=\\\"UTF-8\\\" />\\n <meta http-equiv=\\\"X-UA-Compatible\\\" content=\\\"IE=edge\\\" />\\n <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1.0\\\" />\\n <link rel=\\\"icon\\\" href=\\\"https://kinsta.com/assets/favicon.jpeg\\\" />\\n <link\\n rel=\\\"stylesheet\\\"\\n href=\\\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css\\\"\\n integrity=\\\"sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==\\\"\\n crossorigin=\\\"anonymous\\\"\\n referrerpolicy=\\\"no-referrer\\\"\\n />\\n <link rel=\\\"stylesheet\\\" href=\\\"http://kinsta.com/css/global.css\\\" />\\n <title>J.'s Portfolio</title>\\n </head>\\n <body>\\n <div>\\n {% include \\\"components/navbar.njk\\\" %}\\n {{ content | safe }}\\n {% include \\\"components/footer.njk\\\" %}\\n </div>\\n </body>\\n</html>
\\n\\n\\n\\nКогда вы выполните команду npm start, этот макет не появится, потому что он не был добавлен в шаблон страницы. Создайте шаблон страницы и добавьте этот макет.
\\n\\n\\n\\nВ папке src создайте файл index.njk, который будет служить главной страницей вашего сайта-портфолио. Эта страница будет использовать базовый макет:
\\n\\n\\n\\n---\\nlayout: layouts/base.njk\\ntitle: Home\\n---\\n<h1> This is the {{title}} Page. </h1>
\\n\\n\\n\\nКогда вы выполните команду npm start, ваш статический сайт загрузится на http://localhost:8080/. Вот как будет выглядеть вывод:
\\n\\n\\n\\nШаблон страницы без стилей
\\n\\n\\n\\nТеперь вы знаете, какие существуют шаблоны, как они работают и как их можно использовать вместе. Но вы заметили, что в файле layouts/base.njk файл CSS связан со стилями страницы портфолио, но когда сайт загружается, стили CSS не затрагиваются, потому что файл CSS не добавлен в общую папку.
\\n\\n\\n\\nЧтобы исправить это, необходимо настроить его в файле .eleventy.js с помощью параметра eleventyConfig. Это позволит Eleventy знать о существовании файла(ов) CSS, а также следить за возможными изменениями в файле CSS.
\\n\\n\\n\\nВ папке src вы можете создать папку css для хранения всех CSS-файлов, которые вы будете использовать в своем проекте, но для этой статьи вы можете использовать один CSS-файл – global.css. Затем вы можете настроить папку css так, чтобы она настраивала все файлы внутри папки:
\\n\\n\\n\\neleventyConfig.addPassthroughCopy('src/css');\\neleventyConfig.addWatchTarget('src/css');
\\n\\n\\n\\nТо же самое касается изображений. Если вы добавите любое изображение на свою страницу, вы заметите, что оно не отображается. Чтобы оно отображалось, необходимо настроить папку, в которой хранятся изображения. Давайте создадим папку assets для хранения всех наших изображений и настроим папку assets.
\\n\\n\\n\\neleventyConfig.addPassthroughCopy('src/assets');
\\n\\n\\n\\nВот как теперь будет выглядеть ваш конфигурационный файл:
\\n\\n\\n\\nmodule.exports = function (eleventyConfig) {\\n eleventyConfig.addPassthroughCopy('src/assets');\\n eleventyConfig.addPassthroughCopy('src/css');\\n eleventyConfig.addWatchTarget('src/css');\\n\\n return {\\n dir: {\\n input: 'src',\\n output: 'public',\\n },\\n };\\n};
\\n\\n\\n\\nКогда вы запустите npm start, стилизация CSS будет работать, и ваша домашняя страница будет выглядеть следующим образом:
\\n\\n\\n\\nВнешний вид шаблона после добавления макета
Теперь вы успешно создали макет и добавили его на домашнюю страницу (index.njk). Давайте настроим домашнюю страницу так, чтобы она содержала некоторую информацию о вас, например, дополнительные сведения о вас, ваших навыках и контактную информацию.
\\n\\n\\n\\nВы можете добавить свои коды и разметку непосредственно в шаблон index.njk, но давайте создадим отдельные Частицы для разделов Главная, О себе, навыки и контактная информация.
\\n\\n\\n\\nЭто первый раздел под Navbar, основная цель которого – дать пользователям представление о том, о чем сайт.
\\n\\n\\n\\n<div class=\\\"hero-container\\\">\\n <img src=\\\"https://kinsta.com/blog/eleventy/assets/profile.jpeg\\\" class=\\\"profile-img\\\" alt=\\\"Joe's personal headshot\\\" />\\n <div class=\\\"hero-text\\\">\\n <h1>Hey, I'm Joe 👋</h1>\\n <p>\\n I'm a software developer based in Lagos, Nigeria. I specialize in building (and occasionally designing) exceptional websites, applications, and everything in between.\\n </p>\\n <div class=\\\"social-icons\\\">\\n <a href=\\\"https://twitter.com/olawanle_joel\\\">\\n <i class=\\\"fa-brands fa-twitter\\\"></i>\\n </a>\\n <a href=\\\"https://github.com/olawanlejoel\\\">\\n <i class=\\\"fa-brands fa-github\\\"></i>\\n </a>\\n <a href=\\\"https://www.linkedin.com/in/olawanlejoel/\\\">\\n <i class=\\\"fa-brands fa-linkedin\\\"></i>\\n </a>\\n </div>\\n </div>\\n</div>
\\n\\n\\n\\nНесколько подробностей о вас включены в код выше, а также несколько социальных иконок для подключения ссылок на ваши профили в социальных сетях.
\\n\\n\\n\\nПартикулы героя должны выглядеть следующим образом:
\\n\\n\\n\\nЭкран героя
Вы можете добавить больше контента в раздел Hero, изменить стили в файле css/globals.css или даже создать свою собственную версию этого раздела.
\\n\\n\\n\\nРаздел “О себе” предоставляет людям, которые посещают ваше портфолио, больше информации о вас в любом количестве абзацев. Это может быть отдельная страница, если вам нужно рассказать больше информации.
\\n\\n\\n\\n<div class=\\\"about-container\\\">\\n <h2>About Me</h2>\\n <div class=\\\"flex-about\\\">\\n <div class=\\\"about-text\\\">\\n <p>\\n As a developer, I have always been passionate about creating elegant and effective solutions to complex problems. I have a strong foundation in software development, with a focus on web technologies such as HTML, CSS, and JavaScript. I enjoy working on both the front-end and back-end of applications, and I am always looking for ways to optimize performance, improve user experience, and ensure the highest level of code quality.\\n </p>\\n <p>Throughout my career, I have worked on a wide range of projects, from simple static websites to complex enterprise-level applications. I am experienced in working with a variety of development tools and frameworks, including React, Angular, Vue.js, Node.js, and Laravel. I am always eager to learn and explore new technologies, and I am constantly seeking out opportunities to improve my skills and knowledge.</p>\\n </div>\\n <div class=\\\"about-img\\\">\\n <Image src=\\\"https://kinsta.com/assets/about.jpeg\\\" class=\\\"profile-img\\\" alt=\\\"Joe and animal relaxing and having fun\\\" />\\n </div>\\n </div>\\n</div>
\\n\\n\\n\\nКод содержит информацию о вас (изображение и немного текста). Вот как должен выглядеть раздел “О вас”:
\\n\\n\\n\\nО частицах
Этот раздел используется для отображения технологий, которые вы используете или любите использовать.
\\n\\n\\n\\n<div class=\\\"skills-container\\\">\\n <h2>Skills</h2>\\n <div class=\\\"grid-skills\\\">\\n <div class=\\\"skill-card html\\\">\\n <i class=\\\"fa-brands fa-html5 html-icon\\\"></i>\\n <p>HTML</p>\\n </div>\\n <div class=\\\"skill-card css\\\">\\n <i class=\\\"fa-brands fa-css3-alt css-icon\\\"></i>\\n <p>CSS</p>\\n </div>\\n <div class=\\\"skill-card js\\\">\\n <i class=\\\"fa-brands fa-js-square js-icon\\\"></i>\\n <p>JavaScript</p>\\n </div>\\n <div class=\\\"skill-card react\\\">\\n <i class=\\\"fa-brands fa-react react-icon\\\"></i>\\n <p>React</p>\\n </div>\\n <div class=\\\"skill-card node\\\">\\n <i class=\\\"fa-brands fa-node-js node-icon\\\"></i>\\n <p>Node</p>\\n </div>\\n <div class=\\\"skill-card python\\\">\\n <i class=\\\"fa-brands fa-python python-icon\\\"></i>\\n <p>Python</p>\\n </div>\\n </div>\\n</div>
\\n\\n\\n\\nПриведенный выше код создает карточку для хранения иконки технологии font-awesome и названия каждого навыка. Вы также можете добавить дополнительные стили и изменить код, чтобы сделать его более привлекательным и четким. Вот как должен выглядеть раздел навыков:
\\n\\n\\n\\nНавыки партиципации
Контактные частицы
Поскольку это портфолио, вам следует добавить способ, с помощью которого потенциальные клиенты могут связаться с вами. Одним из способов может быть отправка вам электронного письма.
\\n\\n\\n\\n<div class=\\\"contact-container\\\">\\n <h2>Get In Touch</h2>\\n <p>If you want us to work together, have any question or want me to speak at your event, my inbox is always open. Whether just want to say hi, I'll try my best to get back to you! Cheers!</p>\\n <a href=\\\"https://kinsta.com/blog/eleventy/mailto:[email protected]\\\" class=\\\"cta-btn\\\">Say Hello</a>\\n</div>
\\n\\n\\n\\nЗамените адрес электронной почты в теге a на свой собственный, чтобы кнопка запускала приложение электронной почты, с помощью которого люди смогут отправить вам сообщение.
\\n\\n\\n\\nКонтактные частицы
Теперь вы успешно создали все части для главной страницы. Далее вам нужно включить их в файл index.njk, чтобы они отображались на главной странице:
\\n\\n\\n\\n---\\nlayout: layouts/base.njk\\ntitle: Home\\n---\\n{% include \\\"components/hero.njk\\\" %}\\n{% include \\\"components/about.njk\\\" %}\\n{% include \\\"components/skills.njk\\\" %}\\n{% include \\\"components/contact.njk\\\" %}
\\n\\n\\n\\nКогда вы выполните команду start, на вашей домашней странице будут отображаться все добавленные Коллекции.
\\n\\n\\n\\nВ Eleventy коллекции – это способ сгруппировать связанный контент вместе, чтобы вы могли создавать страницы на его основе. Например, если у вас есть файлы с похожим содержимым (записи блога), хранящиеся в папке блога вашего проекта, вы можете использовать коллекции, чтобы получить их и отобразить список всего содержимого. Также вы можете создать макет для обработки отображения этого содержимого.
\\n\\n\\n\\nКоллекции определяются в файле конфигурации .eleventy.js и могут включать данные из различных источников, таких как файлы markdown или JSON.
\\n\\n\\n\\nДля этого сайта-портфолио создадим каталог projects в каталоге src, чтобы хранить содержимое каждого проекта в формате markdown. Это содержимое будет включать подробную информацию о проекте, решенной проблеме, использованных технологиях, возникших проблемах и извлеченных уроках.
\\n\\n\\n\\nВы можете создать файл markdown с именем проекта (quotes-generator.md) и вставить в него приведенный ниже код:
\\n\\n\\n\\n---\\ntitle: Quotes Generator\\ndescription: \\\"Helps you generates quotes from about 1600 quotes written by different authors . Quotes are automatically copied to your clipboards.\\\"\\ngitHubURL: \\\"https://github.com/olawanlejoel/random-quote-generator\\\"\\nimage: \\\"/assets/quotes-banner.jpeg\\\"\\n---\\n\\nThe quotes generator project is a software tool designed to display random inspirational or thought-provoking quotes to users. This project aims to solve the problem of lack of motivation or inspiration by providing users with a quick and easy way to access inspiring quotes.\\n\\n### Technologies Used\\nThe technologies used in this project include HTML, CSS, and JavaScript. The application utilizes an API to fetch random quotes and display them to the user.\\n\\n### Challenges and Lessons Learned\\nOne of the main challenges faced during this project was designing the user interface to be visually appealing and responsive on different devices. The team had to consider various design elements such as font sizes, colors, and layout to create a user-friendly and aesthetically pleasing interface.\\n\\nAnother challenge was handling errors and edge cases such as network connectivity issues or invalid API responses. The team had to implement error handling and fallback mechanisms to ensure that the application would continue to function smoothly under various conditions.\\n\\nThroughout the project, the team learned valuable lessons about front-end development, such as the importance of clean and efficient code, effective debugging and troubleshooting, and responsive design principles. They also learned the importance of utilizing APIs to access and display data from external sources.\\n\\nOverall, the quotes generator project was a valuable learning experience that allowed the team to develop their technical and creative skills, and create a useful tool for users looking for daily inspiration or motivation.
\\n\\n\\n\\nПримечание: Если вы использовали стартовый шаблон, они у вас уже должны быть, в противном случае вы можете скопировать их из каталога projects нашего стартового шаблона на GitHub.
\\n\\n\\n\\nfrontmatter в верхней части этих файлов, как и в шаблонах, делает значения доступными для вставки в ваши шаблоны.
\\n\\n\\n\\nПоскольку эти файлы Markdown находятся в каталоге src, Eleventy будет рассматривать их как шаблоны и генерировать HTML-страницу для каждого из них. Их URL будет выглядеть примерно так: /projects/quotes-generator.
\\n\\n\\n\\nВнешний вид проекта без верстки
Однако Eleventy не знает, какой макет использовать для этих страниц, потому что у них еще нет значения макета в frontmatter.
\\n\\n\\n\\nДавайте сначала создадим макет для этого содержимого, прежде чем создавать коллекцию и добавлять их в виде списка на специальную страницу проектов.
\\n\\n\\n\\nКак и раньше, создайте файл макета (project.njk) в папке layouts. Чтобы избежать повторений, поскольку этот файл будет использовать HTML-разметку по умолчанию, вы скорректируете макет base.njk, создав блок, обозначающий раздел вашего макета, который будет изменен.
\\n\\n\\n\\n<!DOCTYPE html>\\n<html lang=\\\"en\\\">\\n <head>\\n <meta charset=\\\"UTF-8\\\" />\\n <meta http-equiv=\\\"X-UA-Compatible\\\" content=\\\"IE=edge\\\" />\\n <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1.0\\\" />\\n <link rel=\\\"icon\\\" href=\\\"https://kinsta.com/assets/favicon.jpeg\\\" />\\n <link\\n rel=\\\"stylesheet\\\"\\n href=\\\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css\\\"\\n integrity=\\\"sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==\\\"\\n crossorigin=\\\"anonymous\\\"\\n referrerpolicy=\\\"no-referrer\\\"\\n />\\n <link rel=\\\"stylesheet\\\" href=\\\"http://kinsta.com/css/global.css\\\" />\\n <title>J.'s Portfolio</title>\\n </head>\\n <body>\\n <div>\\n {% include \\\"components/navbar.njk\\\" %}\\n {% block content %} \\n {{ content | safe }}\\n {% endblock %}\\n {% include \\\"components/footer.njk\\\" %}\\n </div>\\n </body>\\n</html>
\\n\\n\\n\\nБлоку присваивается имя content, поскольку в шаблонах может быть много блоков. Теперь вы можете распространить это на макет project.njk, поэтому вам нужно указать только блок content:
\\n\\n\\n\\n{% extends \\\"layouts/base.njk\\\" %}\\n\\n{% block content %}\\n <div class=\\\"project-layout\\\">\\n <h2>{{title}}</h2>\\n <img src=\\\"https://kinsta.com/blog/eleventy/{{image}}\\\" alt=\\\"image\\\" class=\\\"banner-img\\\" />\\n <a href=\\\"{{gitHubURL}}\\\" class=\\\"cta-btn pt-btn\\\">\\n <div class=\\\"small-icons\\\">\\n GitHub <i class=\\\"fa-brands fa-github\\\"></i>\\n </div>\\n </a>\\n {{ content | safe }}\\n </div>\\n{% endblock %}
\\n\\n\\n\\nВ приведенном выше коде вы указываете, как будет отображаться каждый проект. Он получит заголовок, изображение и gitHubURL из frontmatter, а затем добавит другое содержимое с помощью переменной content ({{ content | safe }}).
\\n\\n\\n\\nСледующим шагом будет добавление ключа и значения layout в frontmatter каждого проекта:
\\n\\n\\n\\n---\\nlayout: layouts/project.njk\\ntitle: Quotes Generator\\ndescription: \\\"Helps you generates quotes from about 1600 quotes written by different authors . Quotes are automatically copied to your clipboards.\\\"\\ngitHubURL: \\\"https://github.com/olawanlejoel/random-quote-generator\\\"\\nimage: \\\"/assets/quotes-banner.jpeg\\\"\\n---\\n\\n…
\\n\\n\\n\\nКогда вы перезагрузите URL каждого проекта, например, /projects/quotes-generator, вы заметите, что теперь он использует созданный макет:
\\n\\n\\n\\nВнешний вид проекта с планировкой
Каждый из ваших проектов теперь красиво отображается с заданным макетом, но как люди могут получить доступ к этим проектам? Вам нужно создать список, нажав на который люди смогут перейти к каждому проекту. Здесь на помощь приходят коллекции.
\\n\\n\\n\\nЧтобы использовать коллекцию, вы должны определить ее в файле конфигурации .eleventy.js с помощью метода addCollection().
\\n\\n\\n\\nmodule.exports = function (eleventyConfig) {\\n // …\\n\\n eleventyConfig.addCollection('projects', (collection) => {\\n return collection.getFilteredByGlob('src/projects/*.md');\\n });\\n\\n return {\\n // ...\\n };\\n};
\\n\\n\\n\\nВ приведенном выше коде метод addCollection() используется для определения коллекции под названием projects. Функция обратного вызова, переданная в addCollection(), возвращает все файлы уценки в каталоге projects с помощью метода getFilteredByGlob().
\\n\\n\\n\\nОпределив коллекцию, вы можете использовать ее в шаблоне для генерации страниц на основе этого содержимого. Давайте создадим шаблон страницы projects.njk, который будет использовать макет base.njk, но его содержимым будут проекты из коллекции projects:
\\n\\n\\n\\n---\\nlayout: layouts/base.njk\\ntitle: Projects\\n---\\n<div class=\\\"projects-container\\\">\\n <h2>Projects</h2>\\n <div class=\\\"projects-grid\\\">\\n {% for project in collections.projects %}\\n <div class=\\\"project-card\\\">\\n <div class=\\\"project-header\\\">\\n <i class=\\\"fa-regular fa-folder-open folder-icon\\\"></i>\\n <div class=\\\"small-icons\\\">\\n <a href={{project.data.gitHubURL}}><i class=\\\"fa-brands fa-github\\\"></i></a>\\n </div>\\n </div>\\n <h3>{{project.data.title}}</h3>\\n <p>{{project.data.description}}</p>\\n <a href=\\\"https://kinsta.com/blog/eleventy/{{project.url}}\\\" class=\\\"cta-btn\\\">Read more</a>\\n </div>\\n {% endfor %}\\n </div>\\n</div>
\\n\\n\\n\\nВ приведенном выше коде оператор {% for %} используется для перебора всех проектов в коллекции projects и создания карточки проекта для каждого из них.
\\n\\n\\n\\nВы получите доступ ко всем переменным с помощью project.data.[key]. Например, приведенный выше код отобразит название проекта, его описание и URL GitHub. Вы также можете получить доступ к URL проекта с помощью project.url.
\\n\\n\\n\\nКогда вы выполните команду start и перейдете на страницу проектов, вот как будет выглядеть ваша страница, когда вы добавите много проектов:
\\n\\n\\n\\nСтраница шаблона проектов
Шорткоды – это способ определения пользовательских HTML-тегов или динамических значений JavaScript, которые вы можете повторно использовать в своих шаблонах. Например, вы можете определить шорткод для генерации текущего года и добавить его на свой сайт.
\\n\\n\\n\\nВ конфигурационном файле .eleventy.js можно определить шорткод с помощью метода addShortcode(). Например, следующий код определяет шорткод под названием year:
\\n\\n\\n\\nmodule.exports = function (eleventyConfig) {\\n // ...\\n eleventyConfig.addShortcode('year', () => {\\n return `${new Date().getFullYear()}`;\\n });\\n return {\\n // ...\\n };\\n};
\\n\\n\\n\\nПриведенный выше шорткод year возвращает текущий год, который вы можете добавить в любой шаблон вашего проекта. Например, вместо жесткого кодирования года в футере этого сайта, вы можете добавить его динамически, используя {% year %}, чтобы он обновлялся каждый год:
\\n\\n\\n\\n<hr />\\n<div class=\\\"footer-container\\\">\\n <p>© {% year %} Joel's Portfolio</p>\\n <div class=\\\"social_icons\\\">\\n // ...\\n </div>\\n</div>
\\n\\n\\n\\nКогда страница будет отображена, в теге HTML p будет указан текущий год.
\\n\\n\\n\\nДобавление темы на сайт Eleventy может быть отличным способом быстро настроить внешний вид и ощущение вашего сайта. Официально Eleventy называет темы стартовыми, но следует понимать, что они означают одно и то же. Многие сайты предоставляют бесплатные темы Eleventy, например, официальные стартеры Eleventy и темы Jamstack.
\\n\\n\\n\\nВсе, что вам нужно сделать, это выбрать понравившуюся тему или стартер, затем зайти в ее репозиторий GitHub, чтобы клонировать ее на свою локальную машину. Убедитесь, что вы прочитали документацию по проекту, чтобы узнать, как настроить и кастомизировать проекты.
\\n\\n\\n\\nЗапустите npm install для установки всех используемых пакетов, а затем запустите npm start для локального обслуживания вашего приложения на http://localhost:8080/.
\\n\\n\\n\\nТеперь вам удалось создать стильный статический сайт портфолио с помощью Eleventy. Иметь такой сайт на локальной машине недостаточно. Вы захотите разместить его в Интернете, чтобы поделиться им с кем угодно.
\\n\\n\\n\\nKinsta – это облачная платформа, которая позволяет размещать статические веб-сайты, включая Eleventy. Это можно сделать, разместив свои коды на GitHub, а затем развернув их на Kinsta.
\\n\\n\\n\\nСначала создайте репозиторий на GitHub; это даст вам доступ к URL репозитория. Затем вы можете использовать команды git для переноса своих кодов.
\\n\\n\\n\\nПеред отправкой файлов на GitHub лучше всего создать файл .gitignore, чтобы указать некоторые файлы и папки, которые git должен игнорировать при отправке кода. Создайте файл .gitignore в корневой папке и добавьте в него следующее:
\\n\\n\\n\\n# dependencies\\n/node_modules\\n\\n# run\\n/public
\\n\\n\\n\\nТеперь вы можете инициализировать свой локальный Git-репозиторий, открыв терминал, перейдя в каталог, содержащий ваш проект, и выполнив следующую команду:
\\n\\n\\n\\ngit init
\\n\\n\\n\\nТеперь добавьте ваш код в локальный репозиторий Git с помощью следующей команды:
\\n\\n\\n\\ngit add
\\n\\n\\n\\nТеперь вы можете зафиксировать свои изменения с помощью следующей команды:
\\n\\n\\n\\ngit commit -m \\\"my first commit\\\"
\\n\\n\\n\\nПримечание: Вы можете заменить “мой первый коммит” на краткое сообщение, описывающее ваши изменения.
\\n\\n\\n\\nНаконец, отправьте свой код на GitHub с помощью следующих команд:
\\n\\n\\n\\ngit remote add origin [repository URL]\\ngit push -u origin master
\\n\\n\\n\\nПримечание: Убедитесь, что вы заменили “[URL репозитория]” на URL вашего собственного репозитория GitHub.
\\n\\n\\n\\nПосле выполнения этих шагов ваш код будет размещен на GitHub и доступен по URL вашего репозитория.
\\n\\n\\n\\nТеперь вы можете развернуть сайт на Kinsta!
\\n\\n\\n\\nРазвертывание на Kinsta происходит всего за несколько минут. Начните с приборной панели My Kinsta, чтобы войти в систему или создать свой аккаунт. Затем вы авторизуете Kinsta на GitHub.
\\n\\n\\n\\nЗатем нажмите Приложения на левой боковой панели, затем нажмите Добавить сервис и, наконец, выберите Приложение из выпадающего списка:
\\n\\n\\n\\nРазвертывание на хостинге приложений Kinsta
Появится модальное окно, в котором можно выбрать хранилище, которое вы хотите развернуть. Выберите ветвь, которую вы хотите развернуть, если у вас несколько ветвей в репозитории.
\\n\\n\\n\\nЗатем вы можете присвоить имя этому приложению. Выберите местоположение центра обработки данных из 25 доступных, после чего Kinsta автоматически определит команду запуска.
\\n\\n\\n\\nУспешное развертывание статического сайта Jekyll
Начнется развертывание вашего приложения. В течение нескольких минут будет предоставлена ссылка для доступа к развернутой версии вашего сайта. В данном случае это https://ty-portfolio-lvjy7.kinsta.app/.
\\n\\n\\n\\nСкучные портфолио прочь! Используйте Eleventy для создания статического сайта, который будет кричать “HIRE ME!”.
\\n\\n\\n\\nВ этой статье вы узнали, как создать стильный сайт с помощью Eleventy, о различных способах настройки статического сайта Eleventy с нуля, а также о том, как создать красивый сайт-портфолио.
\\n\\n\\n\\nСоздаете ли вы личный блог, сайт портфолио или интернет-магазин, Eleventy поможет вам достичь ваших целей с минимальными усилиями и максимальным эффектом. Так почему бы не попробовать его сегодня и не посмотреть, что вы можете создать?
\\n\\n\\n\\nЧто вы думаете об Eleventy? Использовали ли вы Eleventy для создания чего-либо? Пожалуйста, не стесняйтесь поделиться с нами своими проектами и опытом в разделе комментариев ниже.
\\n\\n\\n\\n\\n\\n\\n\\r\\nС появлением генераторов статических сайтов (SSG), таких как Eleventy, создание стильного и эффективного статического сайта стало как никогда простым. В этой статье мы рассмотрим, как использовать Eleventy для создания потрясающего и функционального статического сайта портфолио без использования серверного языка или базы данных. Вы также узнаете, как развернуть статический сайт прямо из репозитория GitHub на платформе […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/sozdanie-stilnogo-staticheskogo-veb-sajta-s-eleventy-11ty/\"],\"title\":[0,\"Создание стильного статического веб-сайта с Eleventy (11ty) - Gorlov.\"],\"breadcrumbTitle\":[0,\"Создание стильного статического веб-сайта с Eleventy (11ty)\"],\"description\":[0,\"С появлением генераторов статических сайтов (SSG), таких как Eleventy, создание стильного и эффективного статического сайта стало как никогда простым.\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"С появлением генераторов статических сайтов (SSG), таких как Eleventy, создание стильного и эффективного статического сайта стало как никогда простым.\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Создание стильного статического веб-сайта с Eleventy (11ty) - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T13:05:28+00:00\"],\"url\":[0,\"http://igorlov.loc/sozdanie-stilnogo-staticheskogo-veb-sajta-s-eleventy-11ty/\"]}]}],\"title\":[0,\"Создание стильного статического веб-сайта с Eleventy (11ty)\"],\"uri\":[0,\"/sozdanie-stilnogo-staticheskogo-veb-sajta-s-eleventy-11ty/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"sozdanie-stilnogo-staticheskogo-veb-sajta-s-eleventy-11ty\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Eleventy\\\"],\\\"id\\\":[0,\\\"dGVybToyMDY=\\\"],\\\"uri\\\":[0,\\\"/tag/eleventy/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMjUx\"],\"node\":[0,{\"date\":[0,\"2023-04-21T07:19:43\"],\"content\":[0,\"\\nКаретка (^) – это один из многих символов для создания шаблонов в регулярных выражениях.
\\n\\n\\n\\nКаретка соответствует началу строки или определенной строке. Но это еще не все, что связано с символом каретки (^).
\\n\\n\\n\\nСимвол каретки (^) часто называют “якорем”, поскольку он привязывает шаблон к началу строки или линии. Таким образом, его можно назвать “якорем начала строки”.
\\n\\n\\n\\nДругой якорь — это знак доллара ($), который привязывает шаблон к концу строки, что означает “якорь конца строки”.
\\n\\n\\n\\nЕсть две основные вещи, которые делает символ каретки – он соответствует началу строки или началу строки, и он отрицает набор символов, когда вы помещаете его внутрь квадратных скобок.
\\n\\n\\n\\nКроме того, вы можете захотеть сопоставить сам символ каретки, поскольку он используется и для других целей, помимо регулярных выражений. В этом случае его нужно экранировать.
\\n\\n\\n\\nЧтобы сопоставить начало строки с символом каретки, добавьте его к вашему шаблону.
\\n\\n\\n\\nВ примере ниже я использовал шаблон /^hello\\\\s*world/igm, который будет соответствовать только тексту hello world, находящемуся в начале строки. Любой другой текст hello world между строками или в конце строки не будет соответствовать:
\\n\\n\\n\\nКроме того, шаблон /^c/igm будет соответствовать словам, начинающимся с буквы c, только если они находятся в начале строки:
\\n\\n\\n\\nЕще одна вещь, которую можно сделать с помощью каретки, — это отрицание набора символов. Например, если вы хотите отрицать гласные, вы можете поместить их в набор символов и добавить к ним карету:
\\n\\n\\n\\nВидно, что все гласные не совпали.
\\n\\n\\n\\nКаретку можно использовать и для других целей, например, для экспоненциации в математике или побитового оператора XOR в C++.
\\n\\n\\n\\nЕсли вы хотите сопоставить его, вы должны экранировать его обратной косой чертой \\\\, так как он распознается как метасимвол движками RegEx:
\\n\\n\\n\\nМетасимвол каретки отлично работает в JavaScript. В приведенном ниже фрагменте кода показано, как я тестирую его с некоторыми строками:
\\n\\n\\n\\nconst text1 = `There's hello world in every programming language\\nHello world is what starts many programming language courses.\\nMany programmers don't know any other hello apart from hello world.`;\\n\\nconst text2 = `caret is anchors your pattern to the start of a line\\nTo match the caret itself, you have to escape it.`;\\n\\nconst text3 = '4 raised to power 2 in mathematics is 4 ^ 2';\\n\\nconst re1 = /^hello\\\\s*world/gim;\\nconst re2 = /^c/gim;\\nconst re3 = /\\\\^/;\\n\\nconsole.log(re1.test(text1)); //true\\nconsole.log(re2.test(text2)); //true\\nconsole.log(re3.test(text3)); //true\\n
\\n\\n\\n\\nВ этой статье вы узнали, как можно использовать “якорь начала строки” (метасимвол каретки ^) для привязки шаблона к началу строки или строки как в механизмах RegEx, так и в JavaScript.
\\n\\n\\n\\nЧтобы узнать о якоре конца строки ($), вы можете прочитать эту статью.
\\n\\n\\n\\nСчастливого кодирования!
\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n💸 Отблагодарить автора:
💰 https://www.donationalerts.com/c/woorg_
💰 BTC: 36dLKv5uRozphSQa55w2XgsF42AugPu2QT
💰 ETH: 0x442721192987047eDeEC69Ca1D4c706f9Adb16B3
💰 USDT (TRC20): TR121HxpTDF71TMm9idZkBaZxnjSMcCPWj
Каретка (^) – это один из многих символов для создания шаблонов в регулярных выражениях. Каретка соответствует началу строки или определенной строке. Но это еще не все, что связано с символом каретки (^). Символ каретки (^) часто называют “якорем”, поскольку он привязывает шаблон к началу строки или линии. Таким образом, его можно назвать “якорем начала строки”. […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/chto-oznachaet-simvol-nachala-v-regulyarnyh-vyrazheniyah-metasimvol-karetka-v-regulyarnyh-vyrazheniyah/\"],\"title\":[0,\"Что означает символ начала в регулярных выражениях? Метасимвол "каретка" в регулярных выражениях - Gorlov.\"],\"breadcrumbTitle\":[0,\"Что означает символ начала в регулярных выражениях? Метасимвол “каретка” в регулярных выражениях\"],\"description\":[0,\"Каретка (^) - это один из многих символов для создания шаблонов в регулярных выражениях.\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Каретка (^) - это один из многих символов для создания шаблонов в регулярных выражениях.\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Что означает символ начала в регулярных выражениях? Метасимвол "каретка" в регулярных выражениях - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-04-21T07:19:45+00:00\"],\"url\":[0,\"http://igorlov.loc/chto-oznachaet-simvol-nachala-v-regulyarnyh-vyrazheniyah-metasimvol-karetka-v-regulyarnyh-vyrazheniyah/\"]}]}],\"title\":[0,\"Что означает символ начала в регулярных выражениях? Метасимвол “каретка” в регулярных выражениях\"],\"uri\":[0,\"/chto-oznachaet-simvol-nachala-v-regulyarnyh-vyrazheniyah-metasimvol-karetka-v-regulyarnyh-vyrazheniyah/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"chto-oznachaet-simvol-nachala-v-regulyarnyh-vyrazheniyah-metasimvol-karetka-v-regulyarnyh-vyrazheniyah\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Regex\\\"],\\\"id\\\":[0,\\\"dGVybToxNjg=\\\"],\\\"uri\\\":[0,\\\"/tag/regex/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMjUy\"],\"node\":[0,{\"date\":[0,\"2023-04-21T07:15:27\"],\"content\":[0,\"\\nПосмотрев на Youtube видеоролик “Как работают рекомендательные системы (Netflix/Amazon)” от Art of the Problem, я вдохновился им и захотел создать статью в блоге на эту тему. Итак, здесь мы будем работать над тем, как создать рекомендательную систему с помощью базы данных графов. Для этого мы будем использовать Apache AGE, который является расширением с открытым исходным кодом для PostgreSQL, позволяющим нам создавать узлы и ребра.
\\n\\n\\n\\nУчитывая наблюдения за действиями пользователей в прошлом, нам нужно предсказать, что еще может понравиться пользователю. Мы можем представить предпочтения пользователей графически в виде связей между людьми и вещами, которые они оценивают или о которых имеют мнение, например, фильмами. Подход, который мы будем использовать, называется фильтрацией содержимого, которая использует информацию, известную нам о людях и вещах, в качестве соединительной ткани для рекомендаций.
\\n\\n\\n\\n-- Creating the graph.\\nSELECT create_graph('RecommenderSystem');\\n\\n\\n-- Adding user.\\nSELECT * FROM cypher('RecommenderSystem', $\\n CREATE (:Person {name: 'Abigail'})\\n$) AS (a agtype);\\n\\n\\n-- Adding movies.\\nSELECT * FROM cypher('RecommenderSystem', $\\n CREATE (:Movie {title: 'The Matrix'}),\\n (:Movie {title: 'Shrek'}),\\n (:Movie {title: 'The Blair Witch Project'}),\\n (:Movie {title: 'Jurassic Park'}),\\n (:Movie {title: 'Thor: Love and Thunder'})\\n$) AS (a agtype);\\n\\n\\n-- Adding categories.\\nSELECT * FROM cypher('RecommenderSystem', $\\n CREATE (:Category {name: 'Action'}),\\n (:Category {name: 'Comedy'}),\\n (:Category {name: 'Horror'})\\n$) AS (a agtype);\\n
\\n\\n\\n\\nМы можем представить силу связей с помощью свойства под названием рейтинг на ребрах между пользователями и категориями, а также фильмами и категориями. Этот рейтинг будет варьироваться от 0 до 4, где 0 означает, что пользователь ненавидел фильм, а 4 – что фильм ему понравился. Это также работает для категорий и фильмов, где 0 – меньшая вероятность, а 4 – наибольшая вероятность.
\\n\\n\\n\\nДопустим, Эбигейл имеет рейтинг 3 для комедии, 1 для боевика и 0 для ужасов.
\\n\\n\\n\\n-- User preferences.\\nSELECT * FROM cypher('RecommenderSystem', $\\n MATCH (a:Person {name: 'Abigail'}), (A:Category), (C:Category), (H:Category)\\n WHERE A.name = 'Action' AND C.name = 'Comedy' AND H.name = 'Horror' \\n CREATE (a)-[:RATING {rating: 3}]->(C),\\n (a)-[:RATING {rating: 1}]->(A),\\n (a)-[:RATING {rating: 0}]->(H)\\n$) AS (a agtype);\\n
\\n\\n\\n\\nКаждый фильм также сопоставлен с каждой категорией таким же образом. Например, в “Матрице” нет комедии, много экшена и нет ужасов.
\\n\\n\\n\\n-- The Matrix and it's relationship with Categories.\\nSELECT * FROM cypher('RecommenderSystem', $\\n MATCH (matrix:Movie {title: 'The Matrix'}), (A:Category), (C:Category), (H:Category)\\n WHERE A.name = 'Action' AND C.name = 'Comedy' AND H.name = 'Horror' \\n CREATE (matrix)-[:RATING {rating: 0}]->(C),\\n (matrix)-[:RATING {rating: 4}]->(A),\\n (matrix)-[:RATING {rating: 0}]->(H)\\n$) AS (a agtype);\\n\\n-- Shrek and it's relationship with Categories.\\nSELECT * FROM cypher('RecommenderSystem', $\\n MATCH (shrek:Movie {title: 'Shrek'}), (A:Category), (C:Category), (H:Category)\\n WHERE A.name = 'Action' AND C.name = 'Comedy' AND H.name = 'Horror' \\n CREATE (shrek)-[:RATING {rating: 4}]->(C),\\n (shrek)-[:RATING {rating: 2}]->(A),\\n (shrek)-[:RATING {rating: 0}]->(H)\\n$) AS (a agtype);\\n\\n-- The Blair Witch Project and it's relationship with Categories.\\nSELECT * FROM cypher('RecommenderSystem', $\\n MATCH (witch:Movie {title: 'The Blair Witch Project'}), (A:Category), (C:Category), (H:Category)\\n WHERE A.name = 'Action' AND C.name = 'Comedy' AND H.name = 'Horror' \\n CREATE (witch)-[:RATING {rating: 0}]->(C),\\n (witch)-[:RATING {rating: 0}]->(A),\\n (witch)-[:RATING {rating: 4}]->(H)\\n$) AS (a agtype);\\n\\n-- Jurassic Park and it's relationship with Categories.\\nSELECT * FROM cypher('RecommenderSystem', $\\n MATCH (jurassic:Movie {title: 'Jurassic Park'}), (A:Category), (C:Category), (H:Category)\\n WHERE A.name = 'Action' AND C.name = 'Comedy' AND H.name = 'Horror' \\n CREATE (jurassic)-[:RATING {rating: 1}]->(C),\\n (jurassic)-[:RATING {rating: 3}]->(A),\\n (jurassic)-[:RATING {rating: 0}]->(H)\\n$) AS (a agtype);\\n\\n-- Thor: Love and Thunder and it's relationship with Categories.\\nSELECT * FROM cypher('RecommenderSystem', $\\n MATCH (thor:Movie {title: 'Thor: Love and Thunder'}), (A:Category), (C:Category), (H:Category)\\n WHERE A.name = 'Action' AND C.name = 'Comedy' AND H.name = 'Horror' \\n CREATE (thor)-[:RATING {rating: 4}]->(C),\\n (thor)-[:RATING {rating: 2}]->(A),\\n (thor)-[:RATING {rating: 0}]->(H)\\n$) AS (a agtype);\\n
\\n\\n\\n\\nЧтобы определить, понравится ли кому-то фильм, нужно перемножить все факторы вместе и разделить их на количество категорий, умноженное на 4.
\\n\\n\\n\\n-- The Matrix estimated rating for the user.\\nSELECT e1/(ct*4) AS factor FROM cypher('RecommenderSystem', $\\nMATCH (u:Person)-[e1:RATING]->(v:Category)<-[e2:RATING]-(w:Movie{title: 'The Matrix'}), (c:Category) WITH e1, e2, COUNT(*) AS ct\\nRETURN SUM(e1.rating * e2.rating)::float, ct\\n$) AS (e1 float, ct agtype);\\n\\n factor \\n-------------------\\n 0.333333333333333\\n(1 row)\\n\\n
\\n\\n\\n\\nМы можем представить силу связи между Эбигейл и Матрицей как: [(3 x 0) + (1 x 4) + (0 x 0)] / 12 = 0,3 . По нашим оценкам, фильм ей не очень понравится. Теперь нам нужно собрать данные по всем остальным фильмам, чтобы мы могли показать те, которые больше всего соответствуют ее интересам.
\\n\\n\\n\\n-- Shrek's estimated rating for the user.\\nSELECT e1/(ct*4) AS factor FROM cypher('RecommenderSystem', $\\nMATCH (u:Person)-[e1:RATING]->(v:Category)<-[e2:RATING]-(w:Movie{title: 'Shrek'}), (c:Category) WITH e1, e2, COUNT(*) AS ct\\nRETURN SUM(e1.rating * e2.rating)::float, ct\\n$) AS (e1 float, ct agtype);\\n\\n factor \\n------------------\\n 1.16666666666667\\n(1 row)\\n\\n\\n-- The Blair Witch Project estimated rating for the user.\\nSELECT e1/(ct*4) AS factor FROM cypher('RecommenderSystem', $\\nMATCH (u:Person)-[e1:RATING]->(v:Category)<-[e2:RATING]-(w:Movie{title: 'The Blair Witch Project'}), (c:Category) WITH e1, e2, COUNT(*) AS ct\\nRETURN SUM(e1.rating * e2.rating)::float, ct\\n$) AS (e1 float, ct agtype);\\n\\n factor \\n--------\\n 0.0\\n(1 row)\\n\\n\\n-- Jurassic Park estimated rating for the user.\\nSELECT e1/(ct*4) AS factor FROM cypher('RecommenderSystem', $\\nMATCH (u:Person)-[e1:RATING]->(v:Category)<-[e2:RATING]-(w:Movie{title: 'Jurassic Park'}), (c:Category) WITH e1, e2, COUNT(*) AS ct\\nRETURN SUM(e1.rating * e2.rating)::float, ct\\n$) AS (e1 float, ct agtype);\\n factor \\n--------\\n 0.5\\n(1 row)\\n\\n\\n-- Thor: Love and Thunder estimated rating for the user.\\nSELECT e1/(ct*4) AS factor FROM cypher('RecommenderSystem', $\\nMATCH (u:Person)-[e1:RATING]->(v:Category)<-[e2:RATING]-(w:Movie{title: 'Thor: Love and Thunder'}), (c:Category) WITH e1, e2, COUNT(*) AS ct\\nRETURN SUM(e1.rating * e2.rating)::float, ct\\n$) AS (e1 float, ct agtype);\\n factor \\n------------------\\n 1.16666666666667\\n(1 row)\\n
\\n\\n\\n\\nНесмотря на то, что “Шрек” и “Тор” не являются ее чашкой чая, согласно нашему анализу графиков, Эбигейл предпочтет посмотреть фильмы из нашего списка.
\\n\\n\\n\\nМы показали, как создать рекомендательную систему с графовой базой данных с помощью Apache AGE. Этот подход может быть расширен для реализации более сложных сценариев, таких как включение демографических данных пользователя, истории поиска или связей в социальных сетях. Графовые базы данных хорошо подходят для рекомендательных систем, поскольку они могут легко представлять отношения между пользователями и предметами, а также атрибуты этих сущностей. Кроме того, использование SQL и языка запросов Cypher облегчает работу с большими массивами данных и выполнение сложных запросов. В целом, мы надеемся, что эта статья послужит отправной точкой для тех, кто заинтересован в создании рекомендательной системы с использованием базы данных графов.
\\n\\n\\n\\r\\nПосмотрев на Youtube видеоролик “Как работают рекомендательные системы (Netflix/Amazon)” от Art of the Problem, я вдохновился им и захотел создать статью в блоге на эту тему. Итак, здесь мы будем работать над тем, как создать рекомендательную систему с помощью базы данных графов. Для этого мы будем использовать Apache AGE, который является расширением с открытым исходным […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"],\"breadcrumbs\":[1,\"[[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Главная\\\"],\\\"url\\\":[0,\\\"https://igorlov.ru\\\"]}],[0,{\\\"isHidden\\\":[0,false],\\\"text\\\":[0,\\\"Учебник\\\"],\\\"url\\\":[0,\\\"http://igorlov.loc/category/uchebniki/\\\"]}]]\"],\"canonicalUrl\":[0,\"http://igorlov.loc/rekomendatelnaya-sistema-s-apache-age/\"],\"title\":[0,\"Рекомендательная система с Apache AGE - Gorlov.\"],\"breadcrumbTitle\":[0,\"Рекомендательная система с Apache AGE\"],\"description\":[0,\"Посмотрев на Youtube видеоролик "Как работают рекомендательные системы (Netflix/Amazon)" от Art of the Problem, я вдохновился им и захотел создать статью в\"],\"jsonLd\":[0,{\"raw\":[0,\"\\n\"]}],\"openGraph\":[0,{\"description\":[0,\"Посмотрев на Youtube видеоролик "Как работают рекомендательные системы (Netflix/Amazon)" от Art of the Problem, я вдохновился им и захотел создать статью в\"],\"locale\":[0,\"EN_US\"],\"siteName\":[0,\"Gorlov.\"],\"title\":[0,\"Рекомендательная система с Apache AGE - Gorlov.\"],\"type\":[0,\"article\"],\"updatedTime\":[0,\"2023-05-22T13:05:35+00:00\"],\"url\":[0,\"http://igorlov.loc/rekomendatelnaya-sistema-s-apache-age/\"]}]}],\"title\":[0,\"Рекомендательная система с Apache AGE\"],\"uri\":[0,\"/rekomendatelnaya-sistema-s-apache-age/\"],\"status\":[0,\"publish\"],\"slug\":[0,\"rekomendatelnaya-sistema-s-apache-age\"],\"categories\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}],\"terms\":[0,{\"nodes\":[1,\"[[0,{\\\"name\\\":[0,\\\"Graph\\\"],\\\"id\\\":[0,\\\"dGVybToyMDU=\\\"],\\\"uri\\\":[0,\\\"/tag/graph/\\\"]}],[0,{\\\"name\\\":[0,\\\"Postgres\\\"],\\\"id\\\":[0,\\\"dGVybToxOTU=\\\"],\\\"uri\\\":[0,\\\"/tag/postgres/\\\"]}],[0,{\\\"name\\\":[0,\\\"Учебник\\\"],\\\"id\\\":[0,\\\"dGVybToyOQ==\\\"],\\\"uri\\\":[0,\\\"/category/uchebniki/\\\"]}]]\"]}]}]}],[0,{\"cursor\":[0,\"YXJyYXljb25uZWN0aW9uOjExMjU0\"],\"node\":[0,{\"date\":[0,\"2023-04-21T07:10:21\"],\"content\":[0,\"\\nАтаки по времени — это класс вредоносных атак на продукт, при которых длительность времени, которое требуется вашему приложению для выполнения задачи, приводит к утечке некоторой информации. Возьмем, к примеру, приложение, которое принимает для проверки электронную почту и пароль. Если нет пользователя с указанным адресом электронной почты, оно возвращает ошибку за 5 мс, но если указан действительный адрес электронной почты пользователя с неправильным паролем, оно возвращает ошибку за 500 мс.
\\n\\n\\n\\nДля злоумышленника разница во времени между этими двумя запросами может сделать относительно очевидным, есть ли действительное электронное письмо или нет. Если разница была более тонкой, злоумышленник может сделать много запросов в течение длительного времени и усреднить их, чтобы отличить разные случаи.
\\n\\n\\n\\nМожет показаться, что это не так уж важно, но, допустим, я пытаюсь найти чей-то личный e-mail. У меня есть только его имя, и я знаю, что он подписался на ваш сайт. Я могу перебрать множество вариантов firstname.lastname@gmail.com или lastname{3digitnumber}@gmail.com и так далее, пока не найду их реальный адрес электронной почты.
\\n\\n\\n\\nЧтобы решить эту проблему, нам нужно убедиться, что все пути кода занимают одинаковое количество времени. Это означает, что мы должны избегать раннего возврата в чувствительных частях кодовой базы. В случае, когда мы проверяем электронную почту и пароль пользователя, вместо того, чтобы возвращаться раньше времени, если электронная почта не найдена, мы должны проверить пароль на соответствие жестко заданному значению, а затем вернуть false.
\\n\\n\\n\\nТак, в примере с проверкой электронной почты типичный поток будет выглядеть следующим образом:
\\n\\n\\n\\nЭтот поток хорошо работает, когда предоставлены правильные email и пароль, но он становится уязвимым для атаки по времени в следующем сценарии:
\\n\\n\\n\\nОдин из способов избежать этой уязвимости, как я уже упоминал выше, заключается в том, чтобы заставить и правильные, и неправильные потоки следовать одним и тем же процедурам для более тесного согласования по времени:
\\n\\n\\n\\nЭто гарантирует, что функция занимает одинаковое количество времени для всех входов, что затрудняет злоумышленнику извлечение информации.
\\n\\n\\n\\nХотя вы должны делать все возможное для защиты от временных атак, вы также можете добавить дополнительные средства защиты для обеспечения безопасности. Поскольку тонкие временные атаки основаны на выполнении большого количества запросов, еще одним средством защиты здесь является ограничение скорости. Ограничив количество запросов, мы можем сделать невозможным для злоумышленника различать разные случаи.
\\n\\n\\n\\nПри создании потоков аутентификации в приложениях можно легко упустить из виду такие тонкие уязвимости, как временные атаки в коде. Хотя может показаться странным намеренное замедление работы кода, предотвращение потенциальной утечки личной информации стоит того.
\\n\\n\\n\\r\\nАтаки по времени Атаки по времени — это класс вредоносных атак на продукт, при которых длительность времени, которое требуется вашему приложению для выполнения задачи, приводит к утечке некоторой информации. Возьмем, к примеру, приложение, которое принимает для проверки электронную почту и пароль. Если нет пользователя с указанным адресом электронной почты, оно возвращает ошибку за 5 мс, […]
\\n\"],\"featuredImage\":[0,null],\"seo\":[0,{\"fullHead\":[0,\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n