{"id":1069,"date":"2026-07-10T19:31:06","date_gmt":"2026-07-10T16:31:06","guid":{"rendered":"https:\/\/news.tfeya.ru\/?p=1069"},"modified":"2026-07-10T19:46:36","modified_gmt":"2026-07-10T16:46:36","slug":"%d0%ba%d0%b0%d0%ba-%d1%8f-%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bb-%d1%83%d0%bc%d0%bd%d1%8b%d0%b9-%d0%bf%d0%bb%d0%b0%d0%bd%d0%b8%d1%80%d0%be%d0%b2%d1%89%d0%b8%d0%ba-%d0%bf%d0%b8%d1%82%d0%b0%d0%bd%d0%b8","status":"publish","type":"post","link":"https:\/\/news.tfeya.ru\/?p=1069","title":{"rendered":"\u041a\u0430\u043a \u044f \u0441\u043e\u0437\u0434\u0430\u043b \u0443\u043c\u043d\u044b\u0439 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u043f\u0438\u0442\u0430\u043d\u0438\u044f \u0441 drag-and-drop \u0438 \u0430\u0432\u0442\u043e\u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0441\u0435\u0441\u0441\u0438\u0439"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">\u0425\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u043e\u043f\u044b\u0442\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0438\u0442\u0430\u043d\u0438\u044f \u043d\u0430 \u043d\u0435\u0434\u0435\u043b\u044e. \u0417\u0430\u0434\u0430\u0447\u0430 \u043a\u0430\u0437\u0430\u043b\u0430\u0441\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439: \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044c, \u0431\u043b\u044e\u0434\u0430, \u0438\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b. \u041d\u043e \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0432\u0441\u043f\u043b\u044b\u043b\u043e \u0441\u0442\u043e\u043b\u044c\u043a\u043e \u043d\u044e\u0430\u043d\u0441\u043e\u0432 \u2014 \u043e\u0442 UX \u043d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0435 \u0434\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0435\u0441\u0441\u0438\u044f\u043c\u0438 \u043d\u0430 shared-\u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435, \u2014 \u0447\u0442\u043e \u0440\u0435\u0448\u0438\u043b \u043e\u0444\u043e\u0440\u043c\u0438\u0442\u044c \u044d\u0442\u043e \u0432 \u0441\u0442\u0430\u0442\u044c\u044e.<\/p>\n\n\n\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_82_2 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%A7%D1%82%D0%BE_%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D0%BB%D0%BE%D1%81%D1%8C\" >\u0427\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_%E2%84%961_Shared-%D1%85%D0%BE%D1%81%D1%82%D0%B8%D0%BD%D0%B3_%D0%B8_%D1%81%D0%B5%D1%81%D1%81%D0%B8%D0%B8\" >\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21161: Shared-\u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0438 \u0441\u0435\u0441\u0441\u0438\u0438<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82\" >\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%9F%D0%BE%D1%87%D0%B5%D0%BC%D1%83_%D1%8D%D1%82%D0%BE_%D0%BF%D1%80%D0%BE%D0%B8%D1%81%D1%85%D0%BE%D0%B4%D0%B8%D1%82\" >\u041f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%A0%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_Persistent_Tokens\" >\u0420\u0435\u0448\u0435\u043d\u0438\u0435: Persistent Tokens<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%8B\" >\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u044b<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C\" >\u0411\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_%E2%84%962_Drag-and-drop_%D0%BD%D0%B0_%D1%85%D0%BE%D0%BB%D0%BE%D0%B4%D0%B8%D0%BB%D1%8C%D0%BD%D0%B8%D0%BA%D0%B5\" >\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21162: Drag-and-drop \u043d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0435<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82-2\" >\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%A0%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%A3%D0%BD%D0%B8%D0%B2%D0%B5%D1%80%D1%81%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BE%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%87%D0%B8%D0%BA\" >\u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#UX-%D1%84%D0%B8%D1%88%D0%BA%D0%B8\" >UX-\u0444\u0438\u0448\u043a\u0438<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_%E2%84%963_COLLATE_%D0%B8_%D0%BA%D0%BE%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0\" >\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21163: COLLATE \u0438 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82-3\" >\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%9F%D1%80%D0%B8%D1%87%D0%B8%D0%BD%D0%B0\" >\u041f\u0440\u0438\u0447\u0438\u043d\u0430<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%A0%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5\" >\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%98%D1%81%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8_%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%8B\" >\u0418\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_%E2%84%964_%D0%A4%D0%BE%D1%82%D0%BE_%D0%B1%D0%BB%D1%8E%D0%B4_%D0%B8_%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B7%D0%BA%D0%B0\" >\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21164: \u0424\u043e\u0442\u043e \u0431\u043b\u044e\u0434 \u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82-4\" >\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%A0%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5-2\" >\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_%E2%84%965_%D0%9A%D1%82%D0%BE_%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B6%D0%B0%D0%B5%D1%82_%D0%B1%D0%B0%D0%B7%D0%BE%D0%B2%D1%8B%D0%B5_%D0%B1%D0%BB%D1%8E%D0%B4%D0%B0_%D0%B8_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%B8%D1%80%D1%83%D0%B5%D1%82_%D0%BC%D0%B5%D0%BD%D1%8E\" >\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21165: \u041a\u0442\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0431\u043b\u044e\u0434\u0430 \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u0442 \u043c\u0435\u043d\u044e<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-21\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82-5\" >\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-22\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%A0%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5-3\" >\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-23\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%A7%D1%82%D0%BE_%D0%B5%D1%89%D0%B5_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%BE_%D0%B2_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B8\" >\u0427\u0442\u043e \u0435\u0449\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-24\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%98%D1%82%D0%BE%D0%B3%D0%B8\" >\u0418\u0442\u043e\u0433\u0438<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-25\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%A7%D1%82%D0%BE_%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D0%BB%D0%BE%D1%81%D1%8C-2\" >\u0427\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-26\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%A7%D0%B5%D0%BC%D1%83_%D0%BD%D0%B0%D1%83%D1%87%D0%B8%D0%BB%D1%81%D1%8F\" >\u0427\u0435\u043c\u0443 \u043d\u0430\u0443\u0447\u0438\u043b\u0441\u044f<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-27\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%A7%D1%82%D0%BE_%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE_%D1%83%D0%BB%D1%83%D1%87%D1%88%D0%B8%D1%82%D1%8C\" >\u0427\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-28\" href=\"https:\/\/news.tfeya.ru\/?p=1069\/#%D0%A1%D1%81%D1%8B%D0%BB%D0%BA%D0%B8\" >\u0421\u0441\u044b\u043b\u043a\u0438<\/a><\/li><\/ul><\/nav><\/div>\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%A7%D1%82%D0%BE_%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D0%BB%D0%BE%D1%81%D1%8C\"><\/span>\u0427\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">PWA \u0434\u043b\u044f \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0438\u0442\u0430\u043d\u0438\u044f \u0441 \u0444\u0438\u0447\u0430\u043c\u0438:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Drag-and-drop \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0431\u043b\u044e\u0434 \u043c\u0435\u0436\u0434\u0443 \u0434\u043d\u044f\u043c\u0438<\/li>\n\n\n\n<li>\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0440\u0430\u0441\u0447\u0435\u0442 \u041a\u0411\u0416\u0423 \u0438 \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u043e\u043a\u0443\u043f\u043e\u043a<\/li>\n\n\n\n<li>\u0420\u0430\u0431\u043e\u0442\u0430 \u043d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0435 (\u043a\u0440\u0443\u043f\u043d\u044b\u0439 UI, \u043e\u0444\u043b\u0430\u0439\u043d-\u0440\u0435\u0436\u0438\u043c)<\/li>\n\n\n\n<li>Remember Me \u043d\u0430 30 \u0434\u043d\u0435\u0439 \u0434\u0430\u0436\u0435 \u043d\u0430 shared-\u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435<\/li>\n\n\n\n<li>\u0424\u043e\u0442\u043e \u0431\u043b\u044e\u0434 \u0441 \u043f\u0440\u0435\u0432\u044c\u044e<\/li>\n\n\n\n<li>\u0421\u043a\u0440\u0435\u043c\u0431\u043b-\u0440\u0435\u0436\u0438\u043c (\u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0435 \u0431\u043b\u044e\u0434\u043e, \u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0437\u043d\u0430\u0435\u0448\u044c \u0447\u0442\u043e \u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c)<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ff7\/fdd\/c86\/ff7fddc86f621dca3747e16f8b90928e.jpeg\" alt=\"\u0423\u043c\u043d\u044b\u0439 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a \u2013 \u0414\u0438\u0441\u043f\u043b\u0435\u0439 \u043d\u0430 \u043c\u0430\u0433\u043d\u0438\u0442\u0430\u0445 \u043d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0435 \u0441 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u043c \u043d\u0430 \u043f\u043e\u043b\u043d\u044b\u0439 \u044d\u043a\u0440\u0430\u043d \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c\" title=\"\u0423\u043c\u043d\u044b\u0439 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a \u2013 \u0414\u0438\u0441\u043f\u043b\u0435\u0439 \u043d\u0430 \u043c\u0430\u0433\u043d\u0438\u0442\u0430\u0445 \u043d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0435 \u0441 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u043c \u043d\u0430 \u043f\u043e\u043b\u043d\u044b\u0439 \u044d\u043a\u0440\u0430\u043d \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c\"\/><figcaption class=\"wp-element-caption\">\u0423\u043c\u043d\u044b\u0439 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a \u2013 \u0414\u0438\u0441\u043f\u043b\u0435\u0439 \u043d\u0430 \u043c\u0430\u0433\u043d\u0438\u0442\u0430\u0445 \u043d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0435 \u0441 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u043c \u043d\u0430 \u043f\u043e\u043b\u043d\u044b\u0439 \u044d\u043a\u0440\u0430\u043d \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u0421\u0442\u0435\u043a:&nbsp;<strong>PHP 8.5 + PDO + MySQL + Vanilla JS (\u0431\u0435\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432)<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u0427\u0442\u043e \u044d\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u0442\u0430\u043a\u043e\u0435<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u0420\u0430\u0441\u043f\u0431\u0435\u0440\u0438 \u0434\u043b\u044f \u0437\u0430\u0434\u0430\u0447 \u043d\u0435 \u043f\u043e\u0434\u043e\u0448\u0435\u043b, \u0442\u0430\u043a \u043a\u0430\u043a \u043f\u043e\u0434 \u0440\u0443\u043a\u043e\u0439 \u0431\u044b\u043b\u0430 3 \u0432\u0435\u0440\u0441\u0438\u044f, 5-\u044f \u0441\u0442\u043e\u0438\u0442 \u0443\u0436\u0435 20000 \u0440\u0443\u0431\u043b\u0435\u0439, \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0438\u0435 \u0430\u043d\u0430\u043b\u043e\u0433\u0438 \u0432\u0438\u0434\u0430 Babana Pi \u0441\u0442\u043e\u044f\u0442 \u043e\u0442 9000 \u0440\u0443\u0431\u043b\u0435\u0439, \u0430 Mac Mini \u0437\u0430 54000 \u0440\u0443\u0431\u043b\u0435\u0439 \u0434\u043b\u044f \u044d\u0442\u0438\u0445 \u0446\u0435\u043b\u0435\u0439 \u043f\u043e\u043a\u0443\u043f\u0430\u0442\u044c \u0443\u0436\u0435 \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c. \u0412 \u0438\u0442\u043e\u0433\u0435 \u044f \u0432\u0437\u044f\u043b \u0441\u0442\u0430\u0440\u044b\u0439 \u043d\u043e\u0443\u0442\u0431\u0443\u043a \u0437\u0430 2000 \u0440\u0443\u0431\u043b\u0435\u0439, \u0433\u0434\u0435 \u0441\u0442\u043e\u0438\u0442 \u043d\u043e\u0432\u044b\u0439 SSD 256 \u0413\u0431 \u0438 8 \u0413\u0431 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438 \u043f\u043b\u0430\u043d\u043a\u0430 DDR3. \u0421 \u0420\u0430\u0441\u043f\u0431\u0435\u0440\u0438 \u041f\u0438 3 \u044f \u0443\u0441\u0442\u0430\u043b \u0438\u0433\u0440\u0430\u0442\u044c\u0441\u044f \u0443\u0436\u0435 \u043d\u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u0434\u0435\u043d\u044c \u0438 \u043f\u043e\u043b\u043e\u0436\u0438\u043b \u0435\u0435 \u043d\u0430\u0437\u0430\u0434 \u0432 \u043a\u043e\u0440\u043e\u0431\u043e\u0447\u043a\u0443 \u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u0438\u043a\u043e\u0439.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u0412 \u0438\u0442\u043e\u0433\u0435 \u0432\u0441\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u2013 \u044d\u0442\u043e \u043d\u043e\u0443\u0442\u0431\u0443\u043a, \u0441\u0435\u043d\u0441\u043e\u0440\u043d\u044b\u0439 \u043c\u0430\u0442\u043e\u0432\u044b\u0439 \u0434\u0438\u0441\u043f\u043b\u0435\u0439 \u043d\u0430 15 \u0434\u044e\u0439\u043c\u043e\u0432, \u043e\u0431\u0432\u044f\u0437\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0436\u0435\u043b\u0435\u0437\u0430, \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u043d\u0430 \u043f\u043e\u043b\u043d\u044b\u0439 \u044d\u043a\u0440\u0430\u043d (\u043a\u043d\u043e\u043f\u043a\u0430 \u0432 \u043c\u0435\u043d\u044e).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u041f\u0440\u043e\u0432\u043e\u0434\u0430 \u043d\u0435 \u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432 \u0433\u043e\u0444\u0440\u0443, \u043d\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0435\u043c \u0438 \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u0435\u043c \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0430 \u0441 \u043f\u0440\u043e\u0432\u043e\u0434\u0430\u043c\u0438 \u043d\u0430 \u0441\u0442\u044f\u0436\u043a\u0430\u0445 \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435\u0442 \u043f\u0440\u0438 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u0430 \u043d\u0438\u0436\u0435.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/406\/17e\/cc8\/40617ecc84fb1235718f1d3c2cc12df4.jpg\" alt=\"\u041d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0435 \u0432\u044b\u0440\u043e\u0441\u043b\u0438 \u043d\u043e\u0443\u0442\u0431\u0443\u043a \u0438 \u0434\u0438\u0441\u043f\u043b\u0435\u0439, \u043d\u043e \u0432\u0440\u043e\u0434\u0435 \u0441\u0442\u0430\u043b\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0436\u0438\u0442\u044c\" title=\"\u041d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0435 \u0432\u044b\u0440\u043e\u0441\u043b\u0438 \u043d\u043e\u0443\u0442\u0431\u0443\u043a \u0438 \u0434\u0438\u0441\u043f\u043b\u0435\u0439, \u043d\u043e \u0432\u0440\u043e\u0434\u0435 \u0441\u0442\u0430\u043b\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0436\u0438\u0442\u044c\"\/><figcaption class=\"wp-element-caption\">\u041d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0435 \u0432\u044b\u0440\u043e\u0441\u043b\u0438 \u043d\u043e\u0443\u0442\u0431\u0443\u043a \u0438 \u0434\u0438\u0441\u043f\u043b\u0435\u0439, \u043d\u043e \u0432\u0440\u043e\u0434\u0435 \u0441\u0442\u0430\u043b\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0436\u0438\u0442\u044c<\/figcaption><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_%E2%84%961_Shared-%D1%85%D0%BE%D1%81%D1%82%D0%B8%D0%BD%D0%B3_%D0%B8_%D1%81%D0%B5%D1%81%D1%81%D0%B8%D0%B8\"><\/span>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21161: Shared-\u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0438 \u0441\u0435\u0441\u0441\u0438\u0438<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82\"><\/span>\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u043e\u0438\u0442 \u043d\u0430 \u043e\u0431\u044b\u0447\u043d\u043e\u043c shared-\u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c (\u043c\u043e\u044f \u043f\u043e\u0434\u0440\u0443\u0433\u0430) \u0437\u0430\u0445\u043e\u0434\u0438\u0442 \u0441 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0430, \u0430 \u043d\u0435 \u0441\u0438\u0434\u0438\u0442 \u0432 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0435, \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u0435\u0442 \u043f\u0438\u0442\u0430\u043d\u0438\u0435 \u043d\u0430 \u043d\u0435\u0434\u0435\u043b\u044e \u0438&#8230; \u0447\u0435\u0440\u0435\u0437 24 \u043c\u0438\u043d\u0443\u0442\u044b \u0441\u0435\u0441\u0441\u0438\u044f \u0441\u043b\u0435\u0442\u0430\u0435\u0442. \u041a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u0432\u0432\u043e\u0434\u0438\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c \u2014 \u044d\u0442\u043e UX \u0443\u0440\u043e\u0432\u043d\u044f &#171;\u0432\u044b\u043a\u043b\u044e\u0447\u0438 \u0438 \u0432\u043a\u043b\u044e\u0447\u0438&#187;.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/cc8\/893\/e78\/cc8893e781cf098d6ce05735cf122030.png\" alt=\"\u0424\u043e\u0440\u043c\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438\" title=\"\u0424\u043e\u0440\u043c\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438\"\/><figcaption class=\"wp-element-caption\">\u0424\u043e\u0440\u043c\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%9F%D0%BE%D1%87%D0%B5%D0%BC%D1%83_%D1%8D%D1%82%D0%BE_%D0%BF%D1%80%D0%BE%D0%B8%D1%81%D1%85%D0%BE%D0%B4%D0%B8%D1%82\"><\/span>\u041f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u041d\u0430 shared-\u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><code>session.gc_maxlifetime<\/code>\u00a0= 1440 \u0441\u0435\u043a\u0443\u043d\u0434 (24 \u043c\u0438\u043d\u0443\u0442\u044b) \u2014 \u0434\u0435\u0444\u043e\u043b\u0442 PHP<\/li>\n\n\n\n<li>\u041f\u0430\u043f\u043a\u0430\u00a0<code>\/tmp<\/code>\u00a0\u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u0447\u0438\u0441\u0442\u0438\u0442\u0441\u044f \u0445\u043e\u0441\u0442\u0435\u0440\u043e\u043c<\/li>\n\n\n\n<li>\u041f\u0440\u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441\u0435\u0441\u0441\u0438\u0438 \u0442\u0435\u0440\u044f\u044e\u0442\u0441\u044f<\/li>\n<\/ol>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%A0%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_Persistent_Tokens\"><\/span>\u0420\u0435\u0448\u0435\u043d\u0438\u0435: Persistent Tokens<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u041f\u0440\u043e\u0441\u0442\u043e \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0442\u044c&nbsp;<code>session.gc_maxlifetime<\/code>&nbsp;\u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u2014 \u0445\u043e\u0441\u0442\u0435\u0440 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043f\u043e\u0447\u0438\u0441\u0442\u0438\u0442&nbsp;<code>\/tmp<\/code>. \u041d\u0443\u0436\u0435\u043d \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c&nbsp;<strong>Remember Me<\/strong>&nbsp;\u0447\u0435\u0440\u0435\u0437 \u0442\u043e\u043a\u0435\u043d\u044b \u0432 \u0411\u0414<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>\/\/ === \u0410\u0412\u0422\u041e\u041b\u041e\u0413\u0418\u041d \u041f\u041e REMEMBER_ME COOKIE ===<br><strong>if<\/strong> (empty($_SESSION['user_id']) &amp;&amp; !empty($_COOKIE['remember_me'])) {<br>    $parts = explode(':', $_COOKIE['remember_me']);<br>    <strong>if<\/strong> (count($parts) === 2) {<br>        $selector = $parts[0];<br>        $validator = $parts[1];<br><br>    \/\/ \u0418\u0449\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0432 \u0411\u0414<br>    $stmt = $pdo-&gt;prepare(\"<br>        SELECT rt.id, rt.user_id, rt.hashed_validator, rt.expires_at,<br>               u.family_id, u.name, u.login, f.name as family_name<br>        FROM remember_tokens rt<br>        JOIN users u ON rt.user_id = u.id<br>        JOIN families f ON u.family_id = f.id<br>        WHERE rt.selector = ? AND rt.expires_at &gt; NOW()<br>        LIMIT 1<br>    \");<br>    $stmt-&gt;execute([$selector]);<br>    $token = $stmt-&gt;fetch();<br>    <br>    <strong>if<\/strong> ($token &amp;&amp; hash_equals($token['hashed_validator'], hash('sha256', $validator))) {<br>        \/\/ \u0422\u043e\u043a\u0435\u043d \u0432\u0430\u043b\u0438\u0434\u0435\u043d \u2014 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0441\u0435\u0441\u0441\u0438\u044e<br>        $_SESSION['user_id'] = $token['user_id'];<br>        $_SESSION['family_id'] = $token['family_id'];<br>        $_SESSION['user_name'] = $token['name'];<br>        $_SESSION['family_name'] = $token['family_name'];<br>        <br>        \/\/ Sliding expiration: \u043f\u0440\u043e\u0434\u043b\u0435\u0432\u0430\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u043d\u0430 30 \u0434\u043d\u0435\u0439<br>        $newSelector = bin2hex(random_bytes(16));<br>        $newValidator = bin2hex(random_bytes(32));<br>        $newHashedValidator = hash('sha256', $newValidator);<br>        $newExpires = date('Y-m-d H:i:s', strtotime('+30 days'));<br>        <br>        $updateStmt = $pdo-&gt;prepare(\"<br>            UPDATE remember_tokens <br>            SET selector = ?, hashed_validator = ?, expires_at = ?<br>            WHERE id = ?<br>        \");<br>        $updateStmt-&gt;execute([$newSelector, $newHashedValidator, $newExpires, $token['id']]);<br>        <br>        \/\/ \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c cookie<br>        setcookie('remember_me', $newSelector . ':' . $newValidator, [<br>            'expires' =&gt; time() + (30 * 24 * 60 * 60),<br>            'path' =&gt; '\/',<br>            'httponly' =&gt; true,<br>            'samesite' =&gt; 'Lax',<br>            'secure' =&gt; false  \/\/ \u2190 false \u0434\u043b\u044f HTTP<br>        ]);<br>    }<br>}<br><\/code><a href=\"https:\/\/sourcecraft.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u041e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u0441<\/a><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%8B\"><\/span>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u044b<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>CREATE TABLE remember_tokens (\n    id INT AUTO_INCREMENT PRIMARY KEY,\n    user_id INT NOT NULL,\n    selector VARCHAR(64) NOT NULL UNIQUE,\n    hashed_validator VARCHAR(255) NOT NULL,\n    expires_at DATETIME NOT NULL,\n    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\n    INDEX idx_selector (selector),\n    INDEX idx_user (user_id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;<\/code><a href=\"https:\/\/sourcecraft.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u041e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u0441<\/a><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C\"><\/span>\u0411\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>\u0423\u0433\u0440\u043e\u0437\u0430<\/th><th>\u0417\u0430\u0449\u0438\u0442\u0430<\/th><\/tr><tr><td>\u041f\u0435\u0440\u0435\u0445\u0432\u0430\u0442 cookie<\/td><td><code>httponly=true<\/code>&nbsp;(JS \u043d\u0435 \u0447\u0438\u0442\u0430\u0435\u0442)<\/td><\/tr><tr><td>CSRF<\/td><td><code>samesite=Lax<\/code><\/td><\/tr><tr><td>\u041a\u0440\u0430\u0436\u0430 \u0442\u043e\u043a\u0435\u043d\u0430<\/td><td>Validator \u0445\u0435\u0448\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u0411\u0414<\/td><\/tr><tr><td>\u0411\u0440\u0443\u0442\u0444\u043e\u0440\u0441<\/td><td>Selector + Validator (64+64 \u0431\u0438\u0442\u0430 \u044d\u043d\u0442\u0440\u043e\u043f\u0438\u0438)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/strong>&nbsp;\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0437\u0430\u0445\u043e\u0434\u0438\u0442 \u0440\u0430\u0437 \u0432 \u043c\u0435\u0441\u044f\u0446, \u0432\u0441\u0451 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_%E2%84%962_Drag-and-drop_%D0%BD%D0%B0_%D1%85%D0%BE%D0%BB%D0%BE%D0%B4%D0%B8%D0%BB%D1%8C%D0%BD%D0%B8%D0%BA%D0%B5\"><\/span>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21162: Drag-and-drop \u043d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0435<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82-2\"><\/span>\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u041d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a \u043c\u0430\u0433\u043d\u0438\u0442\u0430\u043c\u0438 \u0437\u0430\u043a\u0440\u0435\u043f\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0438\u0441\u043f\u043b\u0435\u0439 \u0441 \u0442\u0430\u0447\u0441\u043a\u0440\u0438\u043d\u043e\u043c. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u0435\u0440\u0435\u0442\u0430\u0441\u043a\u0438\u0432\u0430\u0442\u044c \u0431\u043b\u044e\u0434\u0430 \u043c\u0435\u0436\u0434\u0443 \u0434\u043d\u044f\u043c\u0438. \u041d\u043e:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u0422\u0430\u0447-\u0441\u043e\u0431\u044b\u0442\u0438\u044f \u2260 \u043c\u044b\u0448\u044c<\/li>\n\n\n\n<li>\u041d\u0443\u0436\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0442\u044c \u043a\u043b\u0438\u043a \u043e\u0442 drag<\/li>\n\n\n\n<li>\u041d\u0430 \u0442\u0430\u0447-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u0445 \u0441\u043a\u0440\u043e\u043b\u043b \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0443\u0435\u0442 \u0441 drag<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/27b\/d5d\/b6d\/27bd5db6dc7ca25585c78a39cecc9bea.png\" alt=\"\u041f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0431\u043b\u044e\u0434\u0430 \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u0439 \u0434\u0435\u043d\u044c \u0438 \u0432 \u0434\u0440\u0443\u0433\u043e\u0439 \u043f\u0440\u0438\u0435\u043c \u043f\u0438\u0449\u0438\" title=\"\u041f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0431\u043b\u044e\u0434\u0430 \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u0439 \u0434\u0435\u043d\u044c \u0438 \u0432 \u0434\u0440\u0443\u0433\u043e\u0439 \u043f\u0440\u0438\u0435\u043c \u043f\u0438\u0449\u0438\"\/><figcaption class=\"wp-element-caption\">\u041f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0431\u043b\u044e\u0434\u0430 \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u0439 \u0434\u0435\u043d\u044c \u0438 \u0432 \u0434\u0440\u0443\u0433\u043e\u0439 \u043f\u0440\u0438\u0435\u043c \u043f\u0438\u0449\u0438<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%A0%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%A3%D0%BD%D0%B8%D0%B2%D0%B5%D1%80%D1%81%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BE%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%87%D0%B8%D0%BA\"><\/span>\u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<pre class=\"wp-block-preformatted\"><code><strong>const<\/strong> initDnD = () =&gt; {<br>    <strong>const<\/strong> board = $('#board');<br>    <strong>const<\/strong> THRESHOLD = 5; \/\/ \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439 \u0434\u043b\u044f \u043e\u0442\u043b\u0438\u0447\u0438\u044f \u043a\u043b\u0438\u043a\u0430 \u043e\u0442 drag<br>    <strong>let<\/strong> dragTarget = <strong>null<\/strong>;<br>    <strong>let<\/strong> startX = 0, startY = 0;<br>    <strong>let<\/strong> moved = false;<br>    <br>    <strong>const<\/strong> onStart = e =&gt; {<br>        <strong>const<\/strong> dish = e.target.closest('.dish');<br>        <strong>if<\/strong> (!dish) <strong>return<\/strong>;<br>        <br>        dragTarget = {<br>            el: dish,<br>            id: parseInt(dish.dataset.id),<br>            dishId: parseInt(dish.dataset.dish)<br>        };<br>        <br>        <strong>const<\/strong> pt = e.touches ? e.touches[0] : e;<br>        startX = pt.clientX;<br>        startY = pt.clientY;<br>        moved = false;<br>        <br>        dish.style.opacity = '0.6';<br>        dish.style.transition = 'none';<br>        <br>        document.addEventListener('mousemove', onMove);<br>        document.addEventListener('touchmove', onMove, { passive: false });<br>        document.addEventListener('mouseup', onEnd);<br>        document.addEventListener('touchend', onEnd);<br>    };<br>    <br>    <strong>const<\/strong> onMove = e =&gt; {<br>        <strong>if<\/strong> (!dragTarget) <strong>return<\/strong>;<br>        <br>        <strong>const<\/strong> pt = e.touches ? e.touches[0] : e;<br>        <strong>const<\/strong> dx = pt.clientX - startX;<br>        <strong>const<\/strong> dy = pt.clientY - startY;<br>        <br>        \/\/ \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c: \u044d\u0442\u043e drag \u0438\u043b\u0438 \u043a\u043b\u0438\u043a?<br>        <strong>if<\/strong> (!moved &amp;&amp; Math.hypot(dx, dy) &gt; THRESHOLD) {<br>            moved = true;<br>        }<br>        <br>        <strong>if<\/strong> (moved) {<br>            <strong>if<\/strong> (e.cancelable) e.preventDefault();<br>            <br>            dragTarget.el.style.transform = `translate(${dx}px, ${dy}px) scale(1.03)`;<br>            <br>            \/\/ \u041f\u043e\u0434\u0441\u0432\u0435\u0442\u043a\u0430 drop-\u0437\u043e\u043d\u044b<br>            $$('.drop-zone').forEach(zone =&gt; {<br>                <strong>const<\/strong> rect = zone.getBoundingClientRect();<br>                <strong>const<\/strong> over = (<br>                    pt.clientX &gt;= rect.left &amp;&amp;<br>                    pt.clientX &lt;= rect.right &amp;&amp;<br>                    pt.clientY &gt;= rect.top &amp;&amp;<br>                    pt.clientY &lt;= rect.bottom<br>                );<br>                zone.classList.toggle('over', over);<br>            });<br>        }<br>    };<br>    <br>    <strong>const<\/strong> onEnd = () =&gt; {<br>        document.removeEventListener('mousemove', onMove);<br>        document.removeEventListener('touchmove', onMove);<br>        document.removeEventListener('mouseup', onEnd);<br>        document.removeEventListener('touchend', onEnd);<br>        <br>        <strong>if<\/strong> (!dragTarget) <strong>return<\/strong>;<br>        <br>        dragTarget.el.style.opacity = '';<br>        dragTarget.el.style.transform = '';<br>        dragTarget.el.style.transition = '';<br>        <br>        <strong>if<\/strong> (moved) {<br>            \/\/ \u042d\u0442\u043e \u0431\u044b\u043b drag \u2192 \u0438\u0449\u0435\u043c drop-\u0437\u043e\u043d\u0443<br>            <strong>const<\/strong> dropZone = $('.drop-zone.over');<br>            <strong>if<\/strong> (dropZone) {<br>                <strong>const<\/strong> toType = parseInt(dropZone.dataset.t);<br>                <strong>const<\/strong> toDay = parseInt(dropZone.dataset.d);<br>                <br>                api('move_item', {<br>                    item_id: dragTarget.id,<br>                    to_type: toType,<br>                    to_day: toDay<br>                }).then(() =&gt; {<br>                    loadWeek().then(render);<br>                    toast('\u2713 \u041f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u043e', 'ok');<br>                });<br>            }<br>        } <strong>else<\/strong> {<br>            \/\/ \u042d\u0442\u043e \u0431\u044b\u043b \u043a\u043b\u0438\u043a \u2192 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0443 \u0431\u043b\u044e\u0434\u0430<br>            openDishDetail(dragTarget.dishId);<br>        }<br>        <br>        $$('.drop-zone').forEach(z =&gt; z.classList.remove('over'));<br>        dragTarget = <strong>null<\/strong>;<br>    };<br>    <br>    board.addEventListener('mousedown', onStart);<br>    board.addEventListener('touchstart', onStart, { passive: true });<br>};<\/code><a href=\"https:\/\/sourcecraft.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u041e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u0441<\/a><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"UX-%D1%84%D0%B8%D1%88%D0%BA%D0%B8\"><\/span>UX-\u0444\u0438\u0448\u043a\u0438<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>1. \u041f\u043e\u0440\u043e\u0433 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043d\u0438\u044f (THRESHOLD = 5px)<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u0415\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0435 &lt; 5px \u2192 \u044d\u0442\u043e \u043a\u043b\u0438\u043a<\/li>\n\n\n\n<li>\u0415\u0441\u043b\u0438 \u2265 5px \u2192 \u044d\u0442\u043e drag<\/li>\n\n\n\n<li>\u041d\u0430 \u0442\u0430\u0447-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u0445 \u044d\u0442\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e: \u043f\u0430\u043b\u0435\u0446 \u0432\u0441\u0435\u0433\u0434\u0430 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0434\u0440\u043e\u0436\u0438\u0442<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>2. \u0412\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u0430\u044f \u0441\u0432\u044f\u0437\u044c<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>.dish {<br>    transition: transform 0.15s, opacity 0.15s;<br>}<br><br>.dish.dragging {<br>    opacity: 0.6;<br>    transform: scale(1.03);<br>    z-index: 100;<br>    box-shadow: 0 8px 24px rgba(0,0,0,0.3);<br>}<br><br>.drop-zone.over {<br>    height: 40px;<br>    background: rgba(0, 184, 148, 0.2);<br>    border: 2px dashed var(--g);<br>    border-radius: 6px;<br>}<\/code><a href=\"https:\/\/sourcecraft.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u041e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u0441<\/a><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>3. \u041e\u0442\u043c\u0435\u043d\u0430 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u043f\u0440\u0438 drag<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code><strong>if<\/strong> (e.cancelable) e.preventDefault();<\/code><a href=\"https:\/\/sourcecraft.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u041e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u0441<\/a><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u0411\u0435\u0437 \u044d\u0442\u043e\u0433\u043e \u043d\u0430 \u0442\u0430\u0447-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u0445 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441\u043a\u0440\u043e\u043b\u043b\u0438\u0442\u0441\u044f \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043f\u0435\u0440\u0435\u0442\u0430\u0441\u043a\u0438\u0432\u0430\u043d\u0438\u0435\u043c \u0431\u043b\u044e\u0434\u0430.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_%E2%84%963_COLLATE_%D0%B8_%D0%BA%D0%BE%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0\"><\/span>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21163: COLLATE \u0438 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82-3\"><\/span>\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u041f\u0440\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0448\u0438\u0431\u043a\u0430:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>SQLSTATE[42000]: COLLATION 'utf8mb4_unicode_ci' is not valid \nfor CHARACTER SET 'utf8mb3'<\/code><a href=\"https:\/\/sourcecraft.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u041e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u0441<\/a><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%9F%D1%80%D0%B8%D1%87%D0%B8%D0%BD%D0%B0\"><\/span>\u041f\u0440\u0438\u0447\u0438\u043d\u0430<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u0412 \u0411\u0414 \u043f\u043e\u043b\u044f&nbsp;<code>login<\/code>&nbsp;\u0438&nbsp;<code>email<\/code>&nbsp;\u0438\u043c\u0435\u044e\u0442 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0443&nbsp;<code>utf8mb3<\/code>, \u0430 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u044f \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u043b&nbsp;<code>COLLATE utf8mb4_unicode_ci<\/code>. MySQL \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c utf8mb4 collation \u043a utf8mb3 \u043f\u043e\u043b\u044e.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%A0%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5\"><\/span>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u041c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u0444\u043e\u0440\u043c\u0430\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 COLLATE, \u044d\u0442\u043e \u0442\u043e\u0447\u043d\u043e \u043f\u043e\u043a\u0430\u0436\u0435\u0442, \u0432 \u0447\u0435\u043c \u0435\u0449\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430, \u0435\u0441\u043b\u0438 \u0443\u0436\u0435 \u043d\u0430\u043f\u0440\u043e\u0447\u044c \u0432\u044b\u043c\u0443\u0447\u0438\u043b \u0441\u0442\u0440\u043e\u0447\u043a\u0443 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043d\u0435 \u043c\u043e\u0436\u0435\u0448\u044c \u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u0438\u0441\u043a\u043e\u043c\u043e\u0433\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>$u = db_row($pdo, \"\n    SELECT u.*, f.name fn \n    FROM users u \n    JOIN families f ON u.family_id = f.id \n    WHERE COALESCE(u.login, '') = ? COLLATE utf8mb4_unicode_ci \n       OR u.email = ? COLLATE utf8mb4_unicode_ci\n\", [$identifier, $identifier]);<\/code><a href=\"https:\/\/sourcecraft.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u041e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u0441<\/a><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%98%D1%81%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8_%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%8B\"><\/span>\u0418\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>ALTER TABLE users \nCONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n\nALTER TABLE remember_tokens \nCONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;<\/code><a href=\"https:\/\/sourcecraft.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u041e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u0441<\/a><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_%E2%84%964_%D0%A4%D0%BE%D1%82%D0%BE_%D0%B1%D0%BB%D1%8E%D0%B4_%D0%B8_%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B7%D0%BA%D0%B0\"><\/span>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21164: \u0424\u043e\u0442\u043e \u0431\u043b\u044e\u0434 \u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82-4\"><\/span>\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0445\u043e\u0447\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u043e\u0442\u043e \u0431\u043b\u044e\u0434\u0430. \u041d\u043e:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u0424\u043e\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c\u0441\u044f \u0432 \u043f\u0430\u043f\u043a\u0443\u00a0<code>\/dishes_photos\/<\/code><\/li>\n\n\n\n<li>\u041d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0440\u0430\u0437\u043c\u0435\u0440 (\u043c\u0430\u043a\u0441 5 \u041c\u0411) \u0438 \u0442\u0438\u043f (\u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f)<\/li>\n\n\n\n<li>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u043e\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0432 \u0441\u0435\u0442\u043a\u0435 \u043d\u0435\u0434\u0435\u043b\u0438<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%A0%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5-2\"><\/span>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Backend (api.php):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code><strong>if<\/strong> ($act == 'upload_dish_photo') {<br>    $dishId = (<strong>int<\/strong>)($_POST['dish_id'] ?? 0);<br>    <strong>if<\/strong> (!$dishId) send(['error' =&gt; '\u041d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d ID \u0431\u043b\u044e\u0434\u0430'], 400);<br>    <br>    <strong>if<\/strong> (!isset($_FILES['photo']) || $_FILES['photo']['error'] !== <strong>UPLOAD_ERR_OK<\/strong>) {<br>        send(['error' =&gt; '\u0424\u0430\u0439\u043b \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d'], 400);<br>    }<br>    <br>    $file = $_FILES['photo'];<br>    <br>    \/\/ \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0440\u0430\u0437\u043c\u0435\u0440\u0430<br>    <strong>if<\/strong> ($file['size'] &gt; 5 * 1024 * 1024) {<br>        send(['error' =&gt; '\u0420\u0430\u0437\u043c\u0435\u0440 \u0444\u0430\u0439\u043b\u0430 \u043f\u0440\u0435\u0432\u044b\u0448\u0430\u0435\u0442 5 \u041c\u0411'], 400);<br>    }<br>    <br>    \/\/ \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 MIME-\u0442\u0438\u043f\u0430<br>    $finfo = finfo_open(<strong>FILEINFO_MIME_TYPE<\/strong>);<br>    $mimeType = finfo_file($finfo, $file['tmp_name']);<br>    finfo_close($finfo);<br>    <br>    $allowedTypes = ['image\/jpeg', 'image\/png', 'image\/gif', 'image\/webp'];<br>    <strong>if<\/strong> (!in_array($mimeType, $allowedTypes)) {<br>        send(['error' =&gt; '\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0442\u0438\u043f \u0444\u0430\u0439\u043b\u0430'], 400);<br>    }<br>    <br>    \/\/ \u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435<br>    $extMap = [<br>        'image\/jpeg' =&gt; 'jpg',<br>        'image\/png' =&gt; 'png',<br>        'image\/gif' =&gt; 'gif',<br>        'image\/webp' =&gt; 'webp'<br>    ];<br>    $ext = $extMap[$mimeType];<br>    $filename = 'dish_' . $dishId . '_' . time() . '.' . $ext;<br>    $uploadDir = <strong>__DIR__<\/strong> . '\/dishes_photos\/';<br>    <br>    <strong>if<\/strong> (!is_dir($uploadDir)) {<br>        mkdir($uploadDir, 0755, true);<br>    }<br>    <br>    $targetPath = $uploadDir . $filename;<br>    <br>    <strong>if<\/strong> (!move_uploaded_file($file['tmp_name'], $targetPath)) {<br>        send(['error' =&gt; '\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u0430'], 500);<br>    }<br>    <br>    \/\/ \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0441\u0442\u0430\u0440\u043e\u0435 \u0444\u043e\u0442\u043e<br>    $oldPhoto = db_val($pdo, \"SELECT photo FROM dishes WHERE id=?\", [$dishId]);<br>    <strong>if<\/strong> ($oldPhoto &amp;&amp; file_exists(<strong>__DIR__<\/strong> . '\/dishes_photos\/' . $oldPhoto)) {<br>        unlink(<strong>__DIR__<\/strong> . '\/dishes_photos\/' . $oldPhoto);<br>    }<br>    <br>    \/\/ \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0432 \u0411\u0414<br>    db_exec($pdo, \"UPDATE dishes SET photo=? WHERE id=?\", [$filename, $dishId]);<br>    <br>    send(['success' =&gt; 1, 'photo' =&gt; $filename]);<br>}<\/code><a href=\"https:\/\/sourcecraft.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u041e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u0441<\/a><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Frontend (\u0430\u0432\u0442\u043e\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043f\u043e\u0441\u043b\u0435 \u0432\u044b\u0431\u043e\u0440\u0430):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>window.uploadDishPhoto = dishId =&gt; {<br>    <strong>const<\/strong> input = document.getElementById('e-photo-' + dishId);<br>    <strong>if<\/strong> (!input || !input.files[0]) <strong>return<\/strong>;<br>    <br>    <strong>const<\/strong> file = input.files[0];<br>    <strong>const<\/strong> formData = <strong>new<\/strong> <strong>FormData<\/strong>();<br>    formData.append('photo', file);<br>    formData.append('dish_id', dishId);<br>    <br>    fetch(API + '?action=upload_dish_photo', {<br>        method: 'POST',<br>        body: formData<br>    })<br>    .then(r =&gt; r.json())<br>    .then(data =&gt; {<br>        <strong>if<\/strong> (data.error) <strong>throw<\/strong> <strong>new<\/strong> <strong>Error<\/strong>(data.error);<br>        toast('\u2713 \u0424\u043e\u0442\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043e', 'ok');<br>        <br>        \/\/ \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0438 \u043f\u0435\u0440\u0435\u0440\u0438\u0441\u043e\u0432\u044b\u0432\u0430\u0435\u043c \u0441\u0435\u0442\u043a\u0443<br>        loadDishes().then(() =&gt; {<br>            render();<br>            openDishDetail(dishId);<br>        });<br>    })<br>    .catch(e =&gt; toast('\u041e\u0448\u0438\u0431\u043a\u0430: ' + e.message, 'err'));<br>};<\/code><a href=\"https:\/\/sourcecraft.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u041e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u0441<\/a><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u0412\u043e\u0442 \u0442\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043e \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u0439 &#8212; \u0441\u0442\u0430\u043b\u043e \u044f\u0432\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0435\u0435.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/efc\/0eb\/80b\/efc0eb80b467db87a330c35ad677bf8c.jpg\" alt=\"\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u0439 \u0431\u043b\u044e\u0434\" title=\"\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u0439 \u0431\u043b\u044e\u0434\"\/><figcaption class=\"wp-element-caption\">\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u0439 \u0431\u043b\u044e\u0434<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_%E2%84%965_%D0%9A%D1%82%D0%BE_%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B6%D0%B0%D0%B5%D1%82_%D0%B1%D0%B0%D0%B7%D0%BE%D0%B2%D1%8B%D0%B5_%D0%B1%D0%BB%D1%8E%D0%B4%D0%B0_%D0%B8_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%B8%D1%80%D1%83%D0%B5%D1%82_%D0%BC%D0%B5%D0%BD%D1%8E\"><\/span>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21165: \u041a\u0442\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0431\u043b\u044e\u0434\u0430 \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u0442 \u043c\u0435\u043d\u044e<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82-5\"><\/span>\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0441\u043c\u043e\u0442\u0440\u0438\u0442 \u043d\u0430 \u043f\u0443\u0441\u0442\u0443\u044e \u043d\u0435\u0434\u0435\u043b\u044e \u0438 \u0434\u0443\u043c\u0430\u0435\u0442: &#171;\u0427\u0442\u043e \u043f\u0440\u0438\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c?&#187;. \u041d\u0443\u0436\u0435\u043d \u0440\u0435\u0436\u0438\u043c &#171;\u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0435 \u0431\u043b\u044e\u0434\u043e&#187;. \u042d\u0442\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u0441\u043b\u043e\u0436\u043d\u043e, \u043d\u043e \u043f\u043e\u043a\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0431\u044b\u043b\u0430 \u0431\u043e\u043b\u0435\u0435 \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u043e\u0439: \u043a\u0430\u043a \u043d\u0435 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043a\u0443\u0447\u0438 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0445 \u043e\u0434\u043d\u043e\u0442\u0438\u043f\u043d\u044b\u0445 \u0431\u043b\u044e\u0434 \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e\u0442 \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439?<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%A0%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5-3\"><\/span>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u041c\u0435\u043d\u044e \u0438 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u044b \u0431\u0435\u0440\u0443\u0442\u0441\u044f \u0443 \u0441\u0435\u043c\u044c\u0438 \u21161, \u0442\u043e \u0435\u0441\u0442\u044c \u043f\u043e \u0444\u0430\u043a\u0442\u0443 \u043e\u0442 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u041e\u043d\u0438 \u043d\u0435 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u044b \u043a \u0441\u0435\u043c\u044c\u0435 \u043f\u043e \u0444\u0430\u043a\u0442\u0443, \u043e\u0434\u043d\u0430\u043a\u043e \u0441\u0435\u043c\u044c\u044f \u0438\u0433\u0440\u0430\u0435\u0442 \u0440\u043e\u043b\u044c \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430. \u0414\u0440\u0443\u0433\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0431\u043b\u044e\u0434\u0430, \u043d\u043e \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u043b\u0438 \u0438\u0441\u043f\u043e\u0440\u0442\u0438\u0442\u044c \u0443\u0436\u0435 \u0438\u043c\u0435\u044e\u0449\u0438\u0435\u0441\u044f.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/fbb\/2ce\/34e\/fbb2ce34e308a8aa3a0f484632e77fc3.png\" alt=\"\u0411\u043b\u044e\u0434\u0430 \u0432 \u043c\u0435\u043d\u044e, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a \u0441\u0435\u0431\u0435 \u0432 \u0440\u0430\u0446\u0438\u043e\u043d\" title=\"\u0411\u043b\u044e\u0434\u0430 \u0432 \u043c\u0435\u043d\u044e, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a \u0441\u0435\u0431\u0435 \u0432 \u0440\u0430\u0446\u0438\u043e\u043d\"\/><figcaption class=\"wp-element-caption\">\u0411\u043b\u044e\u0434\u0430 \u0432 \u043c\u0435\u043d\u044e, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a \u0441\u0435\u0431\u0435 \u0432 \u0440\u0430\u0446\u0438\u043e\u043d<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%A7%D1%82%D0%BE_%D0%B5%D1%89%D0%B5_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%BE_%D0%B2_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B8\"><\/span>\u0427\u0442\u043e \u0435\u0449\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u041c\u043e\u0436\u043d\u043e, \u0440\u0430\u0437\u0443\u043c\u0435\u0435\u0442\u0441\u044f, \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u043e\u0435 \u0431\u043b\u044e\u0434\u043e:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3a3\/f0f\/3a6\/3a3f0f3a6686cbf3ada1eeeeb75fd435.png\" alt=\"\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u041c\u043e\u0436\u043d\u043e \u043e\u0442\u043c\u0435\u0447\u0430\u0442\u044c \u0441\u044a\u0435\u0434\u0435\u043d\u043d\u044b\u0435 \u0431\u043b\u044e\u0434\u0430, \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 \u044d\u0442\u043e\u0433\u043e \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u043d\u0443\u0442\u0440\u044c \u0442\u0435\u0431\u044f \u043f\u043e\u043f\u0430\u043b\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u0436\u0438\u0440\u043e\u0432, \u0431\u0435\u043b\u043a\u043e\u0432, \u0443\u0433\u043b\u0435\u0432\u043e\u0434\u043e\u0432, \u0432\u0438\u0442\u0430\u043c\u0438\u043d\u043e\u0432 \u0438 \u043c\u0438\u043d\u0435\u0440\u0430\u043b\u043e\u0432:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1a0\/18c\/a49\/1a018ca49dd83006f43dc058f3b38b77.png\" alt=\"\u041d\u0435\u0445\u0432\u0430\u0442\u043a\u0430 \u043f\u0438\u0449\u0435\u0432\u043e\u0439 \u0446\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0432 \u043f\u0440\u0438\u043d\u044f\u0442\u043e\u0439 \u043f\u0438\u0449\u0435\" title=\"\u041d\u0435\u0445\u0432\u0430\u0442\u043a\u0430 \u043f\u0438\u0449\u0435\u0432\u043e\u0439 \u0446\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0432 \u043f\u0440\u0438\u043d\u044f\u0442\u043e\u0439 \u043f\u0438\u0449\u0435\"\/><figcaption class=\"wp-element-caption\">\u041d\u0435\u0445\u0432\u0430\u0442\u043a\u0430 \u043f\u0438\u0449\u0435\u0432\u043e\u0439 \u0446\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0432 \u043f\u0440\u0438\u043d\u044f\u0442\u043e\u0439 \u043f\u0438\u0449\u0435<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043d\u0435 \u0437\u0430\u043f\u0440\u0435\u0449\u0430\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438\u0435\u043c\u044b \u043f\u0438\u0449\u0438, \u043d\u043e \u0438 \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u0438\u0435\u043c\u0430 \u0442\u0430\u0431\u043b\u0435\u0442\u043e\u043a, \u043f\u0440\u043e\u0442\u0435\u0438\u043d\u043e\u0432 \u0438\u043b\u0438 \u0441\u0442\u0435\u0440\u043e\u0438\u0434\u043e\u0432, \u043d\u043e \u044f \u044d\u0442\u0438\u043c \u043d\u0435 \u0443\u0432\u043b\u0435\u043a\u0430\u044e\u0441\u044c.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/49c\/4c3\/e61\/49c4c3e611ffe678859e00cf3e528cdc.png\" alt=\"\u041f\u0440\u0438\u0435\u043c\u044b \u043f\u0438\u0449\u0438 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0438\u0440\u0443\u0435\u043c\u044b\u0435\" title=\"\u041f\u0440\u0438\u0435\u043c\u044b \u043f\u0438\u0449\u0438 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0438\u0440\u0443\u0435\u043c\u044b\u0435\"\/><figcaption class=\"wp-element-caption\">\u041f\u0440\u0438\u0435\u043c\u044b \u043f\u0438\u0449\u0438 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0438\u0440\u0443\u0435\u043c\u044b\u0435<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u041d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043c\u0435\u043d\u044e \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u043a\u0443\u043f\u043e\u043a. \u0412\u043e\u0442 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0448\u044c \u0442\u044b \u0441 \u0443\u0442\u0440\u0430 \u043a \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0443, \u0441\u043c\u043e\u0442\u0440\u0438\u0448\u044c, \u0447\u0442\u043e \u043d\u0430 \u0437\u0430\u0432\u0442\u0440\u0430\u0445 \u043e\u043c\u043b\u0435\u0442 \u0441 \u043f\u043e\u043c\u0438\u0434\u043e\u0440\u0430\u043c\u0438, \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0448\u044c \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a&#8230; \u0410 \u0442\u0430\u043c \u2013 \u0447\u0442\u043e \u0443\u0433\u043e\u0434\u043d\u043e, \u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435 \u0438\u0441\u043a\u043e\u043c\u043e\u0435. \u041c\u043e\u0436\u043d\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0441 \u0445\u043e\u0434\u0443 \u0436\u0435\u043b\u0430\u043d\u043d\u043e\u0435 \u043d\u0430 \u043d\u0435\u0436\u0435\u043b\u0430\u043d\u043d\u043e\u0435, \u0441\u0432\u0430\u0440\u0438\u0442\u044c \u0440\u0438\u0441, \u043d\u043e \u0435\u0441\u043b\u0438 \u043f\u043e\u0439\u0442\u0438 \u0432 \u043c\u0430\u0433\u0430\u0437\u0438\u043d, \u0442\u043e \u0442\u043e\u0447\u043d\u043e \u043a\u0443\u043f\u0438\u0448\u044c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043d\u0430 \u043d\u0435\u0434\u0435\u043b\u044e \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u044b.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8a8\/1fa\/db7\/8a81fadb7ee4532c51ada387a142a124.png\" alt=\"\u041f\u0440\u043e\u0434\u0443\u043a\u0442\u044b \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f, \u0437\u0430\u0432\u0442\u0440\u0430 \u0438\u043b\u0438 \u043d\u0430 \u043d\u0435\u0434\u0435\u043b\u044e\" title=\"\u041f\u0440\u043e\u0434\u0443\u043a\u0442\u044b \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f, \u0437\u0430\u0432\u0442\u0440\u0430 \u0438\u043b\u0438 \u043d\u0430 \u043d\u0435\u0434\u0435\u043b\u044e\"\/><figcaption class=\"wp-element-caption\">\u041f\u0440\u043e\u0434\u0443\u043a\u0442\u044b \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f, \u0437\u0430\u0432\u0442\u0440\u0430 \u0438\u043b\u0438 \u043d\u0430 \u043d\u0435\u0434\u0435\u043b\u044e<\/figcaption><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%98%D1%82%D0%BE%D0%B3%D0%B8\"><\/span>\u0418\u0442\u043e\u0433\u0438<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%A7%D1%82%D0%BE_%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D0%BB%D0%BE%D1%81%D1%8C-2\"><\/span>\u0427\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/493\/2cc\/6fe\/4932cc6fed3c0233f9e1a57e469f9b86.png\" alt=\"\u041c\u0435\u043d\u044e \u043d\u0430 \u043d\u0435\u0434\u0435\u043b\u044e \u0441 \u043f\u043e\u0434\u0441\u0447\u0435\u0442\u043e\u043c \u043f\u0438\u0449\u0435\u0432\u043e\u0439 \u0446\u0435\u043d\u043d\u043e\u0441\u0442\u0438\" title=\"\u041c\u0435\u043d\u044e \u043d\u0430 \u043d\u0435\u0434\u0435\u043b\u044e \u0441 \u043f\u043e\u0434\u0441\u0447\u0435\u0442\u043e\u043c \u043f\u0438\u0449\u0435\u0432\u043e\u0439 \u0446\u0435\u043d\u043d\u043e\u0441\u0442\u0438\"\/><figcaption class=\"wp-element-caption\">\u041c\u0435\u043d\u044e \u043d\u0430 \u043d\u0435\u0434\u0435\u043b\u044e \u0441 \u043f\u043e\u0434\u0441\u0447\u0435\u0442\u043e\u043c \u043f\u0438\u0449\u0435\u0432\u043e\u0439 \u0446\u0435\u043d\u043d\u043e\u0441\u0442\u0438<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>\u041c\u0435\u0442\u0440\u0438\u043a\u0430<\/th><th>\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435<\/th><\/tr><tr><td>\u0421\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430<\/td><td>~2500 (PHP + JS + CSS)<\/td><\/tr><tr><td>\u0412\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438<\/td><td>3 \u043d\u0435\u0434\u0435\u043b\u0438 (\u0432\u0435\u0447\u0435\u0440\u0430\u043c\u0438)<\/td><\/tr><tr><td>\u0420\u0430\u0437\u043c\u0435\u0440 \u0411\u0414<\/td><td>17 \u0442\u0430\u0431\u043b\u0438\u0446, ~1000 \u0441\u0442\u0440\u043e\u043a<\/td><\/tr><tr><td>\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c<\/td><td>\u041f\u0435\u0440\u0432\u0430\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 &lt; 1\u0441, subsequent &lt; 200\u043c\u0441<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%A7%D0%B5%D0%BC%D1%83_%D0%BD%D0%B0%D1%83%D1%87%D0%B8%D0%BB%D1%81%D1%8F\"><\/span>\u0427\u0435\u043c\u0443 \u043d\u0430\u0443\u0447\u0438\u043b\u0441\u044f<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Remember Me \u043d\u0430 shared-\u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435<\/strong>\u00a0\u2014 \u0442\u043e\u043a\u0435\u043d\u044b \u0432 \u0411\u0414 + sliding expiration<\/li>\n\n\n\n<li><strong>\u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 drag-and-drop<\/strong>\u00a0\u2014 \u043c\u044b\u0448\u044c + \u0442\u0430\u0447, \u043f\u043e\u0440\u043e\u0433 5px<\/li>\n\n\n\n<li><strong>COLLATE<\/strong>\u00a0\u2014 \u043d\u0435 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u044f\u0432\u043d\u043e, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0435\u0444\u043e\u043b\u0442 \u043f\u043e\u043b\u044f<\/li>\n\n\n\n<li><strong>\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0444\u043e\u0442\u043e<\/strong>\u00a0\u2014 FormData + \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 MIME<\/li>\n\n\n\n<li><strong>UX \u043d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0435<\/strong>\u00a0\u2014 \u043a\u0440\u0443\u043f\u043d\u044b\u0435 \u043a\u043d\u043e\u043f\u043a\u0438, \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439<\/li>\n<\/ol>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%A7%D1%82%D0%BE_%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE_%D1%83%D0%BB%D1%83%D1%87%D1%88%D0%B8%D1%82%D1%8C\"><\/span>\u0427\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Service Worker \u0434\u043b\u044f \u043e\u0444\u043b\u0430\u0439\u043d-\u0440\u0435\u0436\u0438\u043c\u0430<\/li>\n\n\n\n<li>Push-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f &#171;\u041f\u043e\u0440\u0430 \u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c \u043e\u0431\u0435\u0434&#187;<\/li>\n\n\n\n<li>\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 \u042f\u043d\u0434\u0435\u043a\u0441.\u041b\u0430\u0432\u043a\u043e\u0439 (\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0437\u0430\u043a\u0430\u0437 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432)<\/li>\n\n\n\n<li>\u0413\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u0432\u0432\u043e\u0434 \u0440\u0435\u0446\u0435\u043f\u0442\u043e\u0432<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"%D0%A1%D1%81%D1%8B%D0%BB%D0%BA%D0%B8\"><\/span>\u0421\u0441\u044b\u043b\u043a\u0438<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u0414\u0435\u043c\u043e:<\/strong>\u00a0\u043f\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044e<\/li>\n\n\n\n<li><strong>GitHub:<\/strong>\u00a0\u043e\u0442\u043a\u0440\u043e\u044e, \u0435\u0441\u043b\u0438 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0435\u0441<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435!<\/strong>&nbsp;\u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u0432\u043e\u043f\u0440\u043e\u0441\u044b \u043f\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u2014 \u0437\u0430\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445. \u0415\u0441\u043b\u0438 \u0441\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u0431\u0435\u0440\u0451\u0442 \u0440\u0435\u0439\u0442\u0438\u043d\u0433 \u2014 \u0432\u044b\u043b\u043e\u0436\u0443 \u043f\u043e\u043b\u043d\u044b\u0439 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>P.S. \u0416\u0435\u043d\u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u0435\u0442 \u043f\u0438\u0442\u0430\u043d\u0438\u0435 \u043d\u0430 \u043c\u0435\u0441\u044f\u0446 \u0432\u043f\u0435\u0440\u0451\u0434. \u0421\u0447\u0438\u0442\u0430\u0439\u0442\u0435, \u043c\u0438\u0441\u0441\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u0422\u0435\u0433\u0438:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/habr.com\/ru\/search\/?target_type=posts&amp;order=relevance&amp;q=[%D0%A3%D0%BC%D0%BD%D1%8B%D0%B9+%D1%85%D0%BE%D0%BB%D0%BE%D0%B4%D0%B8%D0%BB%D1%8C%D0%BD%D0%B8%D0%BA]\">\u0423\u043c\u043d\u044b\u0439 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/search\/?target_type=posts&amp;order=relevance&amp;q=[%D1%8D%D0%BB%D0%B5%D0%BA%D1%82%D1%80%D0%BE%D0%BD%D0%BD%D0%B0%D1%8F+%D0%BA%D0%BD%D0%B8%D0%B3%D0%B0+%D1%80%D0%B5%D1%86%D0%B5%D0%BF%D1%82%D0%BE%D0%B2]\">\u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0430\u044f \u043a\u043d\u0438\u0433\u0430 \u0440\u0435\u0446\u0435\u043f\u0442\u043e\u0432<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/search\/?target_type=posts&amp;order=relevance&amp;q=[%D0%BC%D0%B5%D0%BD%D1%8E+%D0%BD%D0%B0+%D0%B4%D0%B5%D0%BD%D1%8C]\">\u043c\u0435\u043d\u044e \u043d\u0430 \u0434\u0435\u043d\u044c<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/search\/?target_type=posts&amp;order=relevance&amp;q=[%D1%83%D0%BC%D0%BD%D1%8B%D0%B5+%D1%83%D1%81%D1%82%D1%80%D0%BE%D0%B9%D1%81%D1%82%D0%B2%D0%B0]\">\u0443\u043c\u043d\u044b\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/search\/?target_type=posts&amp;order=relevance&amp;q=[%D1%8D%D0%BB%D0%B5%D0%BA%D1%82%D1%80%D0%BE%D0%BD%D0%B8%D0%BA%D0%B0]\">\u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u0438\u043a\u0430<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/search\/?target_type=posts&amp;order=relevance&amp;q=[IT-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D1%8B]\">IT-\u043f\u0440\u043e\u0435\u043a\u0442\u044b<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/search\/?target_type=posts&amp;order=relevance&amp;q=[DIY]\">DIY<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/search\/?target_type=posts&amp;order=relevance&amp;q=[%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F]\">\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/search\/?target_type=posts&amp;order=relevance&amp;q=[%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%BD%D0%BE%D0%B5+%D0%BE%D0%B1%D0%B5%D1%81%D0%BF%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D0%B5]\">\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0435 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u0435<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/search\/?target_type=posts&amp;order=relevance&amp;q=[%D0%B2%D0%B5%D0%B1-%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F]\">\u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u0425\u0430\u0431\u044b:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/habr.com\/ru\/hubs\/DIY\/\">DIY \u0438\u043b\u0438 \u0421\u0434\u0435\u043b\u0430\u0439 \u0441\u0430\u043c<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/hubs\/javascript\/\">JavaScript<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/hubs\/php\/\">PHP<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/hubs\/webdev\/\">\u0412\u0435\u0431-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/habr.com\/ru\/hubs\/home_automation\/\">\u0423\u043c\u043d\u044b\u0439 \u0434\u043e\u043c<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0425\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u043e\u043f\u044b\u0442\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0438\u0442\u0430\u043d\u0438\u044f \u043d\u0430 \u043d\u0435\u0434\u0435\u043b\u044e. \u0417\u0430\u0434\u0430\u0447\u0430 \u043a\u0430\u0437\u0430\u043b\u0430\u0441\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439: \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044c, \u0431\u043b\u044e\u0434\u0430, \u0438\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b. \u041d\u043e \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0432\u0441\u043f\u043b\u044b\u043b\u043e \u0441\u0442\u043e\u043b\u044c\u043a\u043e \u043d\u044e\u0430\u043d\u0441\u043e\u0432 \u2014 \u043e\u0442 UX \u043d\u0430 \u0445\u043e\u043b\u043e\u0434\u0438\u043b\u044c\u043d\u0438\u043a\u0435 \u0434\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0435\u0441\u0441\u0438\u044f\u043c\u0438 \u043d\u0430 shared-\u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435, \u2014 \u0447\u0442\u043e \u0440\u0435\u0448\u0438\u043b \u043e\u0444\u043e\u0440\u043c\u0438\u0442\u044c \u044d\u0442\u043e \u0432 \u0441\u0442\u0430\u0442\u044c\u044e. \u0427\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c PWA \u0434\u043b\u044f \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0438\u0442\u0430\u043d\u0438\u044f \u0441 \u0444\u0438\u0447\u0430\u043c\u0438: \u0421\u0442\u0435\u043a:&nbsp;PHP 8.5 + PDO + MySQL + Vanilla [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1070,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"dsgo_overlay_header":false,"dsgo_overlay_header_text_color":"","dsgo_overlay_skip_top_bar":false,"_designsetgo_exclude_llms":false,"footnotes":""},"categories":[1,12,104,30],"tags":[348,345,253,344,37],"class_list":["post-1069","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-1","category-12","category-104","category-30","tag-348","tag-345","tag-253","tag-344","tag-37"],"aioseo_notices":[],"tf_views":0,"tf_read_time":11,"_links":{"self":[{"href":"https:\/\/news.tfeya.ru\/index.php?rest_route=\/wp\/v2\/posts\/1069","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/news.tfeya.ru\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/news.tfeya.ru\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/news.tfeya.ru\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/news.tfeya.ru\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1069"}],"version-history":[{"count":1,"href":"https:\/\/news.tfeya.ru\/index.php?rest_route=\/wp\/v2\/posts\/1069\/revisions"}],"predecessor-version":[{"id":1071,"href":"https:\/\/news.tfeya.ru\/index.php?rest_route=\/wp\/v2\/posts\/1069\/revisions\/1071"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/news.tfeya.ru\/index.php?rest_route=\/wp\/v2\/media\/1070"}],"wp:attachment":[{"href":"https:\/\/news.tfeya.ru\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1069"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/news.tfeya.ru\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1069"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/news.tfeya.ru\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1069"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}