{"id":345757,"date":"2023-02-21T15:01:59","date_gmt":"2023-02-21T15:01:59","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=345757"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=345757","title":{"rendered":"<span>\u0425\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441-\u0432\u043e\u0440\u043a\u0435\u0440\u0435<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/webt\/zk\/yn\/jy\/zkynjyt9kos5tgsuqzuquydm1au.jpeg\" data-src=\"https:\/\/habrastorage.org\/webt\/zk\/yn\/jy\/zkynjyt9kos5tgsuqzuquydm1au.jpeg\" data-blurred=\"true\"\/>  <\/p>\n<p>  \u041f\u0440\u0438\u0432\u0435\u0442, \u0434\u0440\u0443\u0437\u044c\u044f!<\/p>\n<p>  <\/p>\n<p>\u041d\u0430 \u0434\u043d\u044f\u0445 \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043b <a href=\"https:\/\/habr.com\/ru\/post\/710552\/\">\u044d\u0442\u0443 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e<\/a>, \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u043d\u0443\u044e \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430\u043c \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 (access token) \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435. \u041c\u043e\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u0432\u043b\u0435\u043a \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Service_Worker_API\">\u0441\u0435\u0440\u0432\u0438\u0441-\u0432\u043e\u0440\u043a\u0435\u0440\u0430 (service worker)<\/a> (\u0441\u043c. &#171;\u041f\u043e\u0434\u0445\u043e\u0434 4. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 service worker&#187;), \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044f \u0434\u0430\u0436\u0435 \u043d\u0435 \u0437\u0430\u0434\u0443\u043c\u044b\u0432\u0430\u043b\u0441\u044f \u043e \u0442\u0430\u043a\u043e\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430.<\/p>\n<p>  <\/p>\n<p>\u0421\u0412 \u2014 \u044d\u0442\u043e \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u043c\u0435\u0436\u0434\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c (\u0441\u0432\u043e\u0435\u0433\u043e \u0440\u043e\u0434\u0430 \u043f\u0440\u043e\u043a\u0441\u0438-\u0441\u0435\u0440\u0432\u0435\u0440), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0438 \u043e\u0442\u0432\u0435\u0442\u044b \u0438 \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0442\u0435\u043c \u0438\u043b\u0438 \u0438\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c. \u041e\u043d \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435, \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043f\u043e\u0442\u043e\u043a\u0435 \u0438 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a DOM. \u041a\u043b\u0438\u0435\u043d\u0442 \u0442\u0430\u043a\u0436\u0435 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0421\u0412 \u0438 \u0445\u0440\u0430\u043d\u0438\u043c\u044b\u043c \u0432 \u043d\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u043c. \u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0421\u0412 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u043e\u0444\u043b\u0430\u0439\u043d \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0430\u0436\u043d\u044b\u0445 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432.<\/p>\n<p>  <\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 <a href=\"https:\/\/jwt.io\/\">JSONWebToken<\/a> \u0438 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Cookies\">HTTP Cookie<\/a> \u0441 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441-\u0432\u043e\u0440\u043a\u0435\u0440\u0435.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0442\u0435\u0445, \u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0434, <a href=\"https:\/\/github.com\/harryheman\/Blog-Posts\/tree\/master\/access-token-service-worker\">\u0432\u043e\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439<\/a>.<\/p>\n<p>  <\/p>\n<p>\u0418\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e? \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u043e\u0448\u0443 \u043f\u043e\u0434 \u043a\u0430\u0442.<\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<p>\u0414\u043b\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\"># \u043a\u043b\u043e\u043d\u0438\u0440\u0443\u0435\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 git init my-project cd my-project git remote add origin https:\/\/github.com\/harryheman\/Blog-Posts\/tree\/master\/access-token-service-worker git config core.sparseCheckout true git sparse-checkout set access-token-service-worker git pull origin master  # \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 cd access-token-service-worker  # \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 yarn # \u0438\u043b\u0438 npm i  # \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439 \u0438 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447\u0438 \u0434\u043b\u044f \u0430\u0441\u0441\u0438\u043c\u0435\u0442\u0440\u0438\u0447\u043d\u043e\u0433\u043e \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u044f\/\u0434\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 yarn gen # \u0438\u043b\u0438 npm run gen  # \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b .env \u0438 \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0432 \u043d\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 .env.example # \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u043c\u0435\u043d\u044f\u0442\u044c move .env.example .env  # \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 yarn dev # \u0438\u043b\u0438 npm run dev<\/code><\/pre>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>: \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>npx prisma migrate dev --name init<\/code>, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0430 Prisma \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>npx prisma generate<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f <a href=\"https:\/\/yarnpkg.com\/\">Yarn<\/a> \u0438 <a href=\"https:\/\/nextjs.org\/docs\/api-reference\/create-next-app\">Create Next App<\/a>:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn create next-app access-token-service-worker --typescript<\/code><\/pre>\n<p>  <\/p>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">- prisma - \u0441\u0445\u0435\u043c\u0430, \u043c\u043e\u0434\u0435\u043b\u0438 \u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 prisma - public   - sw.js - \u043b\u043e\u0433\u0438\u043a\u0430 \u0421\u0412 - src   - components - \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b     - CreateTodoForm.tsx - \u0444\u043e\u0440\u043c\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438     - Footer.tsx - \u043f\u043e\u0434\u0432\u0430\u043b     - Header.tsx - \u0448\u0430\u043f\u043a\u0430     - TodoList.tsx - \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447   - pages - \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b     - api - \"\u0441\u0435\u0440\u0432\u0435\u0440\"       - auth - \u0440\u043e\u0443\u0442\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438         - login.ts - \u0440\u043e\u0443\u0442 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438         - logout.ts - \u0432\u044b\u0445\u043e\u0434\u0430 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b         - register.ts - \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438         - user.ts - \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f       - todo.ts - \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447     - _app.tsx     - _document.tsx     - index.tsx - \u0433\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430     - login.tsx - \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438     - register.tsx - \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438   - styles - \u0441\u0442\u0438\u043b\u0438   - utils - \u0443\u0442\u0438\u043b\u0438\u0442\u044b     - authGuard.ts - \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u043c \u0440\u043e\u0443\u0442\u0430\u043c     - cookies.ts - \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u0443\u043a\u0438     - formToObj.ts - \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0444\u043e\u0440\u043c\u044b \u0432 \u043e\u0431\u044a\u0435\u043a\u0442     - generateKeys.js - \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043a\u043b\u044e\u0447\u0435\u0439     - prisma.ts - \u043a\u043b\u0438\u0435\u043d\u0442 prisma     - registerSW.ts - \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0421\u0412     - swr.ts - \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u0445\u0443\u043a\u0438 swr   - types.ts - .env - \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u044b \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f - environment.d.ts - \u0438\u0445 \u0442\u0438\u043f\u044b - ... - \u0434\u0440\u0443\u0433\u0438\u0435 \u0444\u0430\u0439\u043b\u044b<\/code><\/pre>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u044b\u043c: \u043f\u043e\u0441\u043b\u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438\/\u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c\/\u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438. \u0414\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u0445 \u0438 \u0437\u0430\u0434\u0430\u0447\u0430\u0445 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 \u0440\u0435\u043b\u044f\u0446\u0438\u043e\u043d\u043d\u043e\u0439 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 <a href=\"https:\/\/sqlite.org\/\">SQLite<\/a>. \u0412\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0411\u0414 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0431\u044a\u0435\u043a\u0442\u043d\u043e-\u0440\u0435\u043b\u044f\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f <a href=\"https:\/\/www.prisma.io\/\">Prisma<\/a>.<\/p>\n<p>  <\/p>\n<hr\/>\n<p>  <\/p>\n<p>\u041a\u043b\u0438\u0435\u043d\u0442 \u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b:<\/p>\n<p>  <\/p>\n<ul>\n<li><code>POST \/api\/auth\/register<\/code> \u2014 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (\u0437\u0430\u043f\u0438\u0441\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0411\u0414). \u0422\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430:<br \/> \n<ul>\n<li><code>email: string<\/code> \u2014 \u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b;<\/li>\n<li><code>password: string<\/code> \u2014 \u043f\u0430\u0440\u043e\u043b\u044c;<\/li>\n<\/ul>\n<\/li>\n<li><code>POST \/api\/auth\/login<\/code> \u2014 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e (\u0432\u0445\u043e\u0434 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443) \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0422\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0442\u0430\u043a\u043e\u0435 \u0436\u0435;<\/li>\n<li><code>GET \/api\/auth\/logout<\/code> \u2014 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0432\u044b\u0445\u043e\u0434 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b;<\/li>\n<li><code>GET \/api\/auth\/user<\/code> \u2014 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f;<\/li>\n<li><code>GET \/api\/todo<\/code> \u2014 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f;<\/li>\n<li><code>POST \/api\/todo<\/code> \u2014 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438. \u0422\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430:<br \/> \n<ul>\n<li><code>title: string<\/code> \u2014 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438;<\/li>\n<li><code>content: string<\/code> \u2014 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438;<\/li>\n<\/ul>\n<\/li>\n<li><code>DELETE \/api\/todo?id=&lt;todo-id><\/code> \u2014 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438. \u0421\u0442\u0440\u043e\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (query string) \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c id \u0437\u0430\u0434\u0430\u0447\u0438.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0412\u0441\u0435 \u0440\u043e\u0443\u0442\u044b <code>\/api\/auth<\/code>, \u043a\u0440\u043e\u043c\u0435 <code>\/api\/auth\/user<\/code>, \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c\u0438 (\u043e\u0431\u0449\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c\u0438 \u0438\u043b\u0438 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u043c\u0438). \u0412\u0441\u0435 \u0440\u043e\u0443\u0442\u044b <code>\/api\/todo<\/code> \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0437\u0430\u043a\u0440\u044b\u0442\u044b\u043c\u0438 (\u0447\u0430\u0441\u0442\u043d\u044b\u043c\u0438 \u0438\u043b\u0438 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u043c\u0438).<\/p>\n<p>  <\/p>\n<p>\u0412\u0441\u0435 \u0440\u043e\u0443\u0442\u044b <code>\/api\/auth<\/code>, \u043a\u0440\u043e\u043c\u0435 <code>\/api\/auth\/logout<\/code>, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430. \u0421\u0412 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u044d\u0442\u0438 \u043e\u0442\u0432\u0435\u0442\u044b, \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u0442 \u0438\u0437 \u0442\u0435\u043b\u0430 \u043e\u0442\u0432\u0435\u0442\u0430 \u0442\u043e\u043a\u0435\u043d, \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u0432 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0443\u044e \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0443.<\/p>\n<p>  <\/p>\n<p>\u0412\u0441\u0435 \u0440\u043e\u0443\u0442\u044b <code>\/api\/todo<\/code> \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u043d\u0430\u043b\u0438\u0447\u0438\u044f \u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u0442\u043e\u043a\u0435\u043d\u043e\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u2014 <code>Authorization: Bearer &lt;accessToken><\/code>. \u041a\u043b\u0438\u0435\u043d\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u0431\u0435\u0437 \u0442\u043e\u043a\u0435\u043d\u0430. \u0421\u0412 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0432 \u043d\u0435\u0433\u043e \u0442\u043e\u043a\u0435\u043d \u0438\u0437 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439.<\/p>\n<p>  <\/p>\n<p>\u0420\u043e\u0443\u0442 <code>\/api\/auth\/user<\/code> \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043d\u0430\u043b\u0438\u0447\u0438\u044f \u043a\u0443\u043a\u0438 \u0441 \u0442\u043e\u043a\u0435\u043d\u043e\u043c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u041a\u0443\u043a\u0438 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u043b\u044f\u0435\u0442\u0441\u044f \u043a \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u043c\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u043f\u0440\u0438 \u0435\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438.<\/p>\n<p>  <\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043a\u043b\u0438\u0435\u043d\u0442 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043d\u0438 \u043e \u0442\u043e\u043a\u0435\u043d\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 \u0421\u0412, \u043d\u0438 \u043e \u0442\u043e\u043a\u0435\u043d\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 \u043a\u0443\u043a\u0438, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0439 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0443.<\/p>\n<p>  <\/p>\n<hr\/>\n<p>  <\/p>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<p>  <\/p>\n<ol>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0444\u043e\u0440\u043c\u0443 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 (<code>pages\/register.tsx<\/code>):<\/li>\n<\/ol>\n<p>  <\/p>\n<pre><code class=\"javascript\">import formToObj from '@\/utils\/formToObj' import { useUser } from '@\/utils\/swr' import { User } from '@prisma\/client' import { useRouter } from 'next\/router' import { useState } from 'react'  export default function Register() {   const router = useRouter()   const { mutateUser } = useUser()    const [errors, setErrors] = useState&lt;{     email?: boolean   }>({})    const onSubmit: React.FormEventHandler = async (e) => {     e.preventDefault()      \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0444\u043e\u0440\u043c\u044b \u0432 \u0432\u0438\u0434\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430     const formData = formToObj&lt;Pick&lt;User, 'email' | 'password'>>(       e.target as HTMLFormElement     )      try {       \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441       const res = await fetch('\/api\/auth\/register', {         method: 'POST',         body: JSON.stringify(formData)       })        if (!res.ok) {         \/\/ \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0443\u0436\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d         if (res.status === 409) {           return setErrors({ email: true })         }         throw res       }        const userData = (await res.json()) as Pick&lt;User, 'id' | 'email'>       \/\/ \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0443\u0435\u043c \u043a\u044d\u0448 - \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435       mutateUser(userData)        \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443       router.push('\/')     } catch (e) {       console.error(e)     }   }    const onInput = () => {     setErrors({})   }    return (     &lt;>       &lt;form onSubmit={onSubmit} onInput={onInput}>         &lt;label>           Email:{' '}           &lt;input             type='email'             name='email'             pattern='[^@\\s]+@[^@\\s]+\\.[^@\\s]+'             required           \/>           {errors.email &amp;&amp; (             &lt;p style={{ color: 'red' }}>               &lt;small>Email already in use&lt;\/small>             &lt;\/p>           )}         &lt;\/label>         &lt;label>           Password:{' '}           &lt;input type='password' name='password' minLength={6} required \/>{' '}         &lt;\/label>         &lt;button>Register&lt;\/button>       &lt;\/form>     &lt;\/>   ) }<\/code><\/pre>\n<p>  <\/p>\n<ol>\n<li>\u0421\u0435\u0440\u0432\u0435\u0440 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0411\u0414, \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0442\u043e\u043a\u0435\u043d \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u0432 \u043a\u0443\u043a\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 (<code>pages\/api\/auth\/register.ts<\/code>):<\/li>\n<\/ol>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { NextApiHandlerWithCookie } from '@\/types' import cookies from '@\/utils\/cookies' import prisma from '@\/utils\/prisma' import { User } from '@prisma\/client' import argon2 from 'argon2' import { readFileSync } from 'fs' import jwt from 'jsonwebtoken'  \/\/ \u0447\u0438\u0442\u0430\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0437\u0430\u043a\u0440\u044b\u0442\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430 const PRIVATE_KEY = readFileSync('.\/keys\/private_key.pem', 'utf8')  const registerHandler: NextApiHandlerWithCookie = async (req, res) => {   \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430   const data: Pick&lt;User, 'email' | 'password'> = JSON.parse(req.body)    try {     \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f     const existingUser = await prisma.user.findUnique({       where: { email: data.email }     })      \/\/ \u0435\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u043c\u0435\u044e\u0442\u0441\u044f     \/\/ \u0437\u043d\u0430\u0447\u0438\u0442, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0443\u0436\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d     if (existingUser) {       return res.status(409).json({ message: 'Email already in use' })     }      \/\/ \u0445\u044d\u0448\u0438\u0440\u0443\u0435\u043c \u043f\u0430\u0440\u043e\u043b\u044c     const passwordHash = await argon2.hash(data.password)     \/\/ \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u043d\u0430 \u0445\u044d\u0448     data.password = passwordHash      \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f     const newUser = await prisma.user.create({       data,       \/\/ \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f       select: {         id: true,         email: true       }     })      \/\/ \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0437\u0430\u043a\u0440\u044b\u0442\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430     const idToken = await jwt.sign({ userId: newUser.id }, PRIVATE_KEY, {       \/\/ \u0441\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f - 7 \u0434\u043d\u0435\u0439       expiresIn: '7d',       algorithm: 'RS256'     })      \/\/ \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u0435\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0441\u0440\u0435\u0434\u044b \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f     const accessToken = await jwt.sign(       { userId: newUser.id },       process.env.ACCESS_TOKEN_SECRET,       {         \/\/ \u0441\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f - 1 \u0447\u0430\u0441         expiresIn: '1h'       }     )      \/\/ \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0432 \u043a\u0443\u043a\u0438,     \/\/ \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435     res.cookie({       name: process.env.COOKIE_NAME,       value: idToken,       options: {         \/\/ \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e         httpOnly: true,         secure: true,         \/\/ \u043d\u0430\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f         sameSite: true,         maxAge: 1000 * 60 * 60 * 24 * 7,         path: '\/'       }     })      \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430     res.status(200).json({       user: newUser,       accessToken     })   } catch (e) {     console.log(e)     res.status(500).json({ message: 'User register error' })   } }  export default cookies(registerHandler)<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u043e\u0445\u043e\u0436\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c (\u0441\u043c. <code>pages\/login.tsx<\/code> \u0438 <code>pages\/api\/auth\/login.ts<\/code>).<\/p>\n<p>  <\/p>\n<p>\u0427\u0442\u043e \u043a\u0430\u0441\u0430\u0435\u0442\u0441\u044f \u0432\u044b\u0445\u043e\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u0442\u043e \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u044d\u0442\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 (<code>components\/Header.tsx<\/code>) \u0438 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u043a\u0443\u043a\u0438 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 (<code>pages\/api\/auth\/logout.ts<\/code>).<\/p>\n<p>  <\/p>\n<hr\/>\n<p>  <\/p>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<p>  <\/p>\n<ol>\n<li>\u041f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 (<code>pages\/index.tsx<\/code>) \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0437\u0430\u043f\u0440\u043e\u0441 \u043a <code>\/api\/auth\/user<\/code>:<\/li>\n<\/ol>\n<p>  <\/p>\n<pre><code class=\"javascript\">import CreateTodoForm from '@\/components\/CreateTodoForm' import TodoList from '@\/components\/TodoList' import { useUser } from '@\/utils\/swr'  export default function Home() {   \/\/ \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f   const { user } = useUser()    return (     &lt;>       &lt;h1>Welcome, {user ? user.email : 'stranger'}&lt;\/h1>       &lt;CreateTodoForm \/>       &lt;TodoList \/>     &lt;\/>   ) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0435\u0433\u043e \u0437\u0430\u0434\u0430\u0447 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u0445\u0443\u043a\u043e\u0432 <a href=\"https:\/\/swr.vercel.app\/\">SWR<\/a> (<code>utils\/swr.ts<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import type { Todo, User } from '@prisma\/client' import useSWRImmutable from 'swr\/immutable'  function fetcher&lt;T>(   input: RequestInfo | URL,   init?: RequestInit | undefined ): Promise&lt;T> {   return fetch(input, init).then((res) => res.json()) }  \/\/ \u0445\u0443\u043a \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f export function useUser() {   const { data, error, mutate } = useSWRImmutable&lt;Pick&lt;User, 'id' | 'email'>>(     '\/api\/auth\/user',     \/\/ \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043c\u044b \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0443 \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u0438\u0442\u044c \u043a \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u043a\u0443\u043a\u0438     \/\/ \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 `credentials: 'include'`     (url) => fetcher(url, { credentials: 'include' }),     {       onErrorRetry(err, key, config, revalidate, revalidateOpts) {         return false       }     }   )    if (error) {     console.log(error)   }    return {     user: data?.email ? data : undefined,     mutateUser: mutate   } }  \/\/ \u0445\u0443\u043a \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f export function useTodos(shouldFetch: boolean) {   const { data, error, mutate } = useSWRImmutable&lt;     Pick&lt;Todo, 'id' | 'title' | 'content'>[]     \/\/ \u0434\u0430\u043d\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f,     \/\/ \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c \u0447\u0435\u0433\u043e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f `shouldFetch`   >(shouldFetch ? '\/api\/todo' : null, (url) => fetcher(url), {     onErrorRetry(err, key, config, revalidate, revalidateOpts) {       return false     }   })    if (error) {     console.log(error)   }    return {     todos: Array.isArray(data) ? data : [],     mutateTodos: mutate   } }<\/code><\/pre>\n<p>  <\/p>\n<ol>\n<li>\u0421\u0435\u0440\u0432\u0435\u0440 \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u0442 id \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u043a\u0443\u043a\u0438, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u0411\u0414, \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 (<code>pages\/api\/auth\/user.ts<\/code>):<\/li>\n<\/ol>\n<p>  <\/p>\n<pre><code class=\"javascript\">import prisma from '@\/utils\/prisma' import { readFileSync } from 'fs' import jwt from 'jsonwebtoken' import { NextApiHandler } from 'next'  \/\/ \u0447\u0438\u0442\u0430\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430 const PUBLIC_KEY = readFileSync('.\/keys\/public_key.pem', 'utf8')  const userHandler: NextApiHandler = async (req, res) => {   \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438\u0437 \u043a\u0443\u043a\u0438   const idToken = req.cookies[process.env.COOKIE_NAME]   \/\/ \u0435\u0441\u043b\u0438 \u0442\u043e\u043a\u0435\u043d \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442   if (!idToken) {     return res.status(401).json({ message: 'ID token must be provided' })   }    try {     \/\/ \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u0443\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430     const decodedToken = (await jwt.verify(idToken, PUBLIC_KEY)) as unknown as {       userId: string     }      \/\/ \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u0430\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442     if (!decodedToken || !decodedToken.userId) {       return res.status(403).json({ message: 'Invalid token' })     }      \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 id \u0438\u0437 \u043a\u0443\u043a\u0438     const user = await prisma.user.findUnique({       where: { id: decodedToken.userId },       \/\/ \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f       select: {         id: true,         email: true       }     })      \/\/ \u0435\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442     if (!user) {       return res.status(404).json({ message: 'User not found' })     }      \/\/ \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430     const accessToken = await jwt.sign(       { userId: user.id },       process.env.ACCESS_TOKEN_SECRET,       {         expiresIn: '1h'       }     )      \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430     res.status(200).json({ user, accessToken })   } catch (e) {     console.log(e)     res.status(500).json({ message: 'User get error' })   } }  export default userHandler<\/code><\/pre>\n<p>  <\/p>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, \u0432\u0441\u0435 \u0440\u043e\u0443\u0442\u044b <code>\/api\/auth<\/code>, \u043a\u0440\u043e\u043c\u0435 <code>\/api\/auth\/logout<\/code>, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 \u0442\u043e\u043a\u0435\u043d \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u041e\u043d \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0434\u043e\u0439\u0442\u0438 \u0434\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430! \ud83d\ude42<\/p>\n<p>  <\/p>\n<hr\/>\n<p>  <\/p>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447.<\/p>\n<p>  <\/p>\n<p>\u0424\u043e\u0440\u043c\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447 \u0440\u0435\u043d\u0434\u0435\u0440\u044f\u0442\u0441\u044f \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 (<code>pages\/index.tsx<\/code>).<\/p>\n<p>  <\/p>\n<p>\u0424\u043e\u0440\u043c\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c (<code>components\/CreateTodoForm.tsx<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import formToObj from '@\/utils\/formToObj' import { useTodos, useUser } from '@\/utils\/swr' import { Todo } from '@prisma\/client' import { useRef } from 'react'  export default function CreateTodoForm() {   const { user } = useUser()   const { todos, mutateTodos } = useTodos(Boolean(user))   const formRef = useRef&lt;HTMLFormElement | null>(null)    if (!user) return null    const onSubmit: React.FormEventHandler = async (e) => {     e.preventDefault()     \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0444\u043e\u0440\u043c\u044b \u0432 \u0432\u0438\u0434\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430     const formData = formToObj&lt;Pick&lt;Todo, 'title' | 'content'>>(       e.target as HTMLFormElement     )      try {       \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438       const res = await fetch('\/api\/todo', {         method: 'POST',         body: JSON.stringify(formData)       })       if (!res.ok) throw res       const newTodo = (await res.json()) as Pick&lt;         Todo,         'id' | 'title' | 'content' | 'userId'       >       \/\/ \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0443\u0435\u043c \u043a\u044d\u0448 - \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447       mutateTodos([...todos, newTodo])        \/\/ \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u0444\u043e\u0440\u043c\u0443       if (formRef.current) {         formRef.current.reset()       }     } catch (e) {       console.log(e)     }   }    return (     &lt;div>       &lt;h2>New Todo&lt;\/h2>       &lt;form onSubmit={onSubmit} ref={formRef}>         &lt;label>           Title: &lt;input type='text' name='title' required \/>         &lt;\/label>         &lt;label>           Content:{' '}           &lt;textarea name='content' cols={30} rows={5} required>&lt;\/textarea>         &lt;\/label>         &lt;button>Create&lt;\/button>       &lt;\/form>     &lt;\/div>   ) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0421\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447 (<code>components\/TodoList.tsx<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { useTodos, useUser } from '@\/utils\/swr'  export default function TodoList() {   const { user } = useUser()   const { todos, mutateTodos } = useTodos(Boolean(user))    if (!user || !todos.length) return null    const onClick = async (id: string) => {     try {       \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438       const res = await fetch(`\/api\/todo?id=${id}`, {         method: 'DELETE'       })       if (!res.ok) throw res       const newTodos = todos.filter((todo) => todo.id !== id)       \/\/ \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0443\u0435\u043c \u043a\u044d\u0448       mutateTodos(newTodos)     } catch (e) {       console.log(e)     }   }    return (     &lt;div>       &lt;h2>Todo List&lt;\/h2>       &lt;ul>         {todos.map((todo) => (           &lt;li key={todo.id}>             &lt;p>               &lt;b>{todo.title}&lt;\/b>             &lt;\/p>             &lt;p>{todo.content}&lt;\/p>             &lt;button onClick={() => onClick(todo.id)}>X&lt;\/button>           &lt;\/li>         ))}       &lt;\/ul>     &lt;\/div>   ) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0438\u0447\u0435\u0433\u043e \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0433\u043e.<\/p>\n<p>  <\/p>\n<p>\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u044d\u0442\u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 (<code>pages\/api\/todo.ts<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { NextApiRequestWithUserId } from '@\/types' import authGuard from '@\/utils\/authGuard' import prisma from '@\/utils\/prisma' import { Todo } from '@prisma\/client' import { NextApiResponse } from 'next' import nextConnect from 'next-connect'  const todoHandler = nextConnect&lt;NextApiRequestWithUserId, NextApiResponse>()  \/\/ \u0440\u043e\u0443\u0442 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f todoHandler.get(async (req, res) => {   try {     \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u0437 \u0411\u0414     const todos = await prisma.todo.findMany({       where: {         userId: req.userId       }     })      \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0438\u0445     res.status(200).json(todos)   } catch (e) {     console.log(e)     res.status(500).json({ message: 'Todos get error' })   } })  \/\/ \u0440\u043e\u0443\u0442 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438 todoHandler.post(async (req, res) => {   \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u0437 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430   const data: Pick&lt;Todo, 'title' | 'content' | 'userId'> = JSON.parse(req.body)   \/\/ \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 \u0434\u0430\u043d\u043d\u044b\u0435 id \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f   data.userId = req.userId    try {     \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0437\u0430\u0434\u0430\u0447\u0443     const todo = await prisma.todo.create({       data     })     \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0435     res.status(200).json(todo)   } catch (e) {     console.error(e)     res.status(500).json({ message: 'Todo create error' })   } })  \/\/ \u0440\u043e\u0443\u0442 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438 todoHandler.delete(async (req, res) => {   \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c id \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u0437 \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430   const id = req.query.id as string    try {     \/\/ \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0437\u0430\u0434\u0430\u0447\u0443     const todo = await prisma.todo.delete({       where: {         id_userId: {           id,           userId: req.userId         }       }     })     \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0435     res.status(200).json(todo)   } catch (e) {     console.error(e)     res.status(500).json({ message: 'Todo remove error' })   } })  \/\/ \u0432\u0441\u0435 \u0440\u043e\u0443\u0442\u044b \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u043c\u0438 export default authGuard(todoHandler)<\/code><\/pre>\n<p>  <\/p>\n<p>\u0417\u0430\u0449\u0438\u0442\u0430 \u044d\u0442\u0438\u0445 \u0440\u043e\u0443\u0442\u043e\u0432 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a\u0430 <code>utils\/authGuard.ts<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import jwt from 'jsonwebtoken' import { AuthGuardMiddleware } from '..\/types'  const authGuard: AuthGuardMiddleware = (handler) => async (req, res) => {   \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438\u0437 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 - `Authorization: 'Bearer &lt;accessToken>'`   const accessToken = req.headers.authorization?.split(' ')[1]    \/\/ \u0435\u0441\u043b\u0438 \u0442\u043e\u043a\u0435\u043d \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442   if (!accessToken) {     return res.status(403).json({ message: 'Access token must be provided' })   }    try {     \/\/ \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u0443\u0435\u043c \u0442\u043e\u043a\u0435\u043d     const decodedToken = (await jwt.verify(       accessToken,       process.env.ACCESS_TOKEN_SECRET     )) as unknown as {       userId: string     }      \/\/ \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u0430\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442     if (!decodedToken || !decodedToken.userId) {       return res.status(403).json({ message: 'Invalid token' })     }      \/\/ \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c id \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430     req.userId = decodedToken.userId   } catch (e: any) {     console.log(e)     \/\/ \u0435\u0441\u043b\u0438 \u0438\u0441\u0442\u0435\u043a \u0441\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430     if (e.name === 'TokenExpiredError') {       \/\/ \u0441\u0435\u0440\u0432\u0435\u0440 \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u043e\u043d - \u0447\u0430\u0439\u043d\u0438\u043a :)       return res.status(418).json({ message: 'Access token has been expired' })     }     return res.status(403).json({ message: 'Invalid token' })   }    \/\/ \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c\u0443 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443   return handler(req, res) }  export default authGuard<\/code><\/pre>\n<p>  <\/p>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0440\u043e\u0443\u0442\u0430\u043c <code>\/api\/todo<\/code> \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043d\u0435\u0442.<\/p>\n<p>  <\/p>\n<hr\/>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0441\u0430\u043c\u043e\u043c\u0443 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u043c\u0443 \u2014 \u0421\u0412.<\/p>\n<p>  <\/p>\n<p>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0435\u0433\u043e \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (<code>pages\/_app.tsx<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import Footer from '@\/components\/Footer' import Header from '@\/components\/Header' import '@\/styles\/globals.css' import registerSW from '@\/utils\/registerSW' import type { AppProps } from 'next\/app' import { useEffect } from 'react'  export default function App({ Component, pageProps }: AppProps) {   \/\/ \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0421\u0412 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f   useEffect(() => {     if ('serviceWorker' in navigator) {       registerSW()     }   }, [])    return (     &lt;>       &lt;Header \/>       &lt;main>         &lt;Component {...pageProps} \/>       &lt;\/main>       &lt;Footer \/>     &lt;\/>   ) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0421\u0412 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c (<code>utils\/registerSW.ts<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">export default async function registerSW() {   try {     const reg = await navigator.serviceWorker.register('\/sw.js')     console.log(`Registration scope: ${reg.scope}`)   } catch (e) {     console.log(e)   } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041b\u043e\u0433\u0438\u043a\u0430 \u0421\u0412 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>public\/sw.js<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0438 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u044f \u0421\u0412 \u043d\u0430\u0441 \u043d\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0442 \/\/ self.addEventListener('install', (e) => {}) \/\/ self.addEventListener('activate', (e) => {})  \/\/ \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 let accessToken  \/\/ \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 self.addEventListener('fetch', async (e) => {   \/\/ \u043e\u0431\u044a\u0435\u043a\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430   const { request } = e   \/\/ \u0430\u0434\u0440\u0435\u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u0430   const { url } = request    \/\/ \u0435\u0441\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0437\u0430\u043f\u0440\u043e\u0441 \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0443   if (url.startsWith(self.location.origin) &amp;&amp; url.includes('api')) {     \/\/ \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0432\u044b\u0445\u043e\u0434 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b     if (url.includes('logout')) {       \/\/ \u043f\u0440\u043e\u0441\u0442\u043e \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0442\u043e\u043a\u0435\u043d       accessToken = null        \/\/ \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044e\/\u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e     } else if (url.includes('auth')) {       e.respondWith(         (async () => {           \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441           const res = await fetch(request)           \/\/ \u0435\u0441\u043b\u0438 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430           if (!res.ok) {             \/\/ \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043e\u0442\u0432\u0435\u0442             return res           }           \/\/ \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043c\u044b \u043a\u043b\u043e\u043d\u0438\u0440\u0443\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u043e\u0442\u0432\u0435\u0442\u0430           const data = await res.clone().json()           \/\/ \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u0430           accessToken = data.accessToken           \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u043e\u0442\u0432\u0435\u0442\u0435           const { headers, status, statusText } = res.clone()           \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043e\u0442\u0432\u0435\u0442 \u0431\u0435\u0437 \u0442\u043e\u043a\u0435\u043d\u0430 (!) \u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e           return new Response(JSON.stringify(data.user), {             headers,             status,             statusText           })         })()       )     }      \/\/ \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\/\u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438     if (url.includes('todo')) {       e.respondWith(         (async () => {           \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441           \/\/ \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043c\u044b \u043a\u043b\u043e\u043d\u0438\u0440\u0443\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430           \/\/ \u0437\u0434\u0435\u0441\u044c \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0442\u043e\u0433\u043e,           \/\/ \u0447\u0442\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0430\u0448\u0438\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440,           \/\/ \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 `Referer`           res = await fetch(request.clone(), {             headers: {               \/\/ \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438               Authorization: `Bearer ${accessToken}`             }           })            \/\/ \u0435\u0441\u043b\u0438 \u0441\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u0438\u0441\u0442\u0435\u043a           if (res.status === 418) {             \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u0442\u043e\u043a\u0435\u043d             res = await fetch(`${self.location.origin}\/api\/auth\/user`, {               \/\/ \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u043b\u044f\u0435\u043c \u043a \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u043a\u0443\u043a\u0438               credentials: 'include'             })             const data = await res.json()             \/\/ \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u0430             accessToken = data.accessToken              \/\/ \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u043c \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441 \u043d\u043e\u0432\u044b\u043c \u0442\u043e\u043a\u0435\u043d\u043e\u043c             res = await fetch(request.clone(), {               headers: {                 Authorization: `Bearer ${accessToken}`               }             })           }            \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043e\u0442\u0432\u0435\u0442           return res         })()       )     }   } })<\/code><\/pre>\n<p>  <\/p>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, \u0421\u0412 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0434\u0432\u0435 \u0433\u0440\u0443\u043f\u043f\u044b \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432:<\/p>\n<p>  <\/p>\n<ul>\n<li><code>\/api\/auth\/*<\/code> \u2014 \u0438\u0437 \u043e\u0442\u0432\u0435\u0442\u0430 \u043d\u0430 \u044d\u0442\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0421\u0412 \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u0442 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f;<\/li>\n<li><code>\/api\/todo\/*<\/code> \u2014 \u043a \u044d\u0442\u0438\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c \u0421\u0412 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u0442\u043e\u043a\u0435\u043d\u043e\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438 \u043f\u0440\u043e\u0434\u043b\u0435\u0432\u0430\u0435\u0442 \u0441\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041f\u043e\u0436\u0430\u043b\u0443\u0439, \u044d\u0442\u043e \u0432\u0441\u0435, \u0447\u0435\u043c \u044f \u0445\u043e\u0442\u0435\u043b \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0432\u0430\u043c\u0438 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0432\u044b \u0443\u0437\u043d\u0430\u043b\u0438 \u0447\u0442\u043e-\u0442\u043e \u043d\u043e\u0432\u043e\u0435 \u0438 \u043d\u0435 \u0437\u0440\u044f \u043f\u043e\u0442\u0440\u0430\u0442\u0438\u043b\u0438 \u0432\u0440\u0435\u043c\u044f. \u0422\u0430\u043a\u0436\u0435 \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u0430\u044f \u0437\u0434\u0435\u0441\u044c \u0442\u0435\u0445\u043d\u0438\u043a\u0430 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0430\u0448\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0435\u0449\u0435 \u0431\u043e\u043b\u0435\u0435 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u043c.<\/p>\n<p>  <\/p>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0438 happy coding!<\/p>\n<p>  <\/p>\n<hr\/>\n<p>  <\/p>\n<p><a href=\"https:\/\/timeweb.cloud\/?utm_source=habr&amp;utm_medium=banner&amp;utm_campaign=vds-promo-6-rub\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/p-\/u9\/l2\/p-u9l27ynelxi92bcmdxhu76ma8.png\" data-src=\"https:\/\/habrastorage.org\/webt\/p-\/u9\/l2\/p-u9l27ynelxi92bcmdxhu76ma8.png\"\/><\/a><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p> <!----> <!----><\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/timeweb\/blog\/718320\/\"> https:\/\/habr.com\/ru\/company\/timeweb\/blog\/718320\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/webt\/zk\/yn\/jy\/zkynjyt9kos5tgsuqzuquydm1au.jpeg\" data-src=\"https:\/\/habrastorage.org\/webt\/zk\/yn\/jy\/zkynjyt9kos5tgsuqzuquydm1au.jpeg\" data-blurred=\"true\"\/>  <\/p>\n<p>  \u041f\u0440\u0438\u0432\u0435\u0442, \u0434\u0440\u0443\u0437\u044c\u044f!<\/p>\n<p>  <\/p>\n<p>\u041d\u0430 \u0434\u043d\u044f\u0445 \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043b <a href=\"https:\/\/habr.com\/ru\/post\/710552\/\">\u044d\u0442\u0443 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e<\/a>, \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u043d\u0443\u044e \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430\u043c \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 (access token) \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435. \u041c\u043e\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u0432\u043b\u0435\u043a \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Service_Worker_API\">\u0441\u0435\u0440\u0432\u0438\u0441-\u0432\u043e\u0440\u043a\u0435\u0440\u0430 (service worker)<\/a> (\u0441\u043c. &#171;\u041f\u043e\u0434\u0445\u043e\u0434 4. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 service worker&#187;), \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044f \u0434\u0430\u0436\u0435 \u043d\u0435 \u0437\u0430\u0434\u0443\u043c\u044b\u0432\u0430\u043b\u0441\u044f \u043e \u0442\u0430\u043a\u043e\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430.<\/p>\n<p>  <\/p>\n<p>\u0421\u0412 \u2014 \u044d\u0442\u043e \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u043c\u0435\u0436\u0434\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c (\u0441\u0432\u043e\u0435\u0433\u043e \u0440\u043e\u0434\u0430 \u043f\u0440\u043e\u043a\u0441\u0438-\u0441\u0435\u0440\u0432\u0435\u0440), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0438 \u043e\u0442\u0432\u0435\u0442\u044b \u0438 \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0442\u0435\u043c \u0438\u043b\u0438 \u0438\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c. \u041e\u043d \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435, \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043f\u043e\u0442\u043e\u043a\u0435 \u0438 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a DOM. \u041a\u043b\u0438\u0435\u043d\u0442 \u0442\u0430\u043a\u0436\u0435 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0421\u0412 \u0438 \u0445\u0440\u0430\u043d\u0438\u043c\u044b\u043c \u0432 \u043d\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u043c. \u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0421\u0412 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u043e\u0444\u043b\u0430\u0439\u043d \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0430\u0436\u043d\u044b\u0445 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432.<\/p>\n<p>  <\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 <a href=\"https:\/\/jwt.io\/\">JSONWebToken<\/a> \u0438 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Cookies\">HTTP Cookie<\/a> \u0441 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441-\u0432\u043e\u0440\u043a\u0435\u0440\u0435.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0442\u0435\u0445, \u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0434, <a href=\"https:\/\/github.com\/harryheman\/Blog-Posts\/tree\/master\/access-token-service-worker\">\u0432\u043e\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439<\/a>.<\/p>\n<p>  <\/p>\n<p>\u0418\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e? \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u043e\u0448\u0443 \u043f\u043e\u0434 \u043a\u0430\u0442.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-345757","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/345757","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=345757"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/345757\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=345757"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=345757"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=345757"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}