Web-разработка на node.js и express. Изучаем node.js на практике

    Предисловие



    Пришла мне в голову смелая мысль, взяться за написание туториала по разработке на node.js (что-то наподобие ruby.railstutorial.org). Так как я первый раз берусь за подобный труд то для для меня очень важен любой фидбэк. Нужно это — не нужно. Что стоит убрать а на чем остановиться подробнее, в общем любая конструктивная критика. Надеюсь что дело пойдет и вам понравится, в этом случае по мере написания буду выкладывать главы на хабр.

    Вступление



    Приветствую, перед вами небольшой учебник по практической разработке на node.js, с использованием фреймворка express. Я с большим энтузиазмом отношусь к node и сопутствующим технологиям. Node.js в первую очередь привлекает свежестью в подходах к разработке, смелостью и драйвом.

    О том, что такое node.js вы можете прочитать на http://nodejs.org/, если коротко — то это серверная платформа, для выполнения javascript. Так же мы будем использовать express, web-фреймворк построеный на концепции middleware (о том, что это такое, поговорим поподробнее чуть позже)

    В процессе изучения мы познакомимся с различными аспектами web-разработки, такими как использование системы контроля версий, автоматическое тестирование и так далее. В результате по ходу изучения мы разработаем вполне рабочее web-приложение (простенький аналог твиттера)

    Хочется отметить, что очень большое влияние на меня оказал railstutorial, это лучшее пособие по web-разработке, которое я встречал, и мне очень хотелось бы создать нечто подобное для node.js.

    Глава 1. Старт



    В этой главе мы развернем рабочее окружение с нуля, установим все необходимые инструменты, создадим простенькое приложение и даже запустим его на облачном хостинге.

    1.1 Рабочее окружение



    Так как я в основном использую в работе linux, а если точнее — Ubuntu 12.04, основная часть инструкций по установке тех или иных инструментов будет ориентирована на ползователей линукс, но я буду стараться по возможности давать ссылки на инструкции и для других ОС.

    Для того чтобы точно следовать инструкциям в учебнике, вам нужно будет поставить систему контроля версий git, дело в том, что мы не только будем размещать код своих проектов в git, но и устанавливать многие инструменты из репозиториев на гитхабе.

    Так что, первое что мы сделаем — это...

    1.1.1 Установка git



    Пользователи apt-based дистрибутивов могут выполнить в терминале:

    $ sudo apt-get install git-core
    


    Остальные отправляются читать инструкции по адресу http://git-scm.com/book/ch1-4.html

    1.1.2 Установка node.js и npm



    Теперь пришло время поставить последнюю стабильню версию node.js и npm (установщик пакетов для node). Инструкции по установке разных ОС можно найти здесь. Для установки на ubuntu выполняем:

    $ sudo apt-get install python-software-properties
    $ sudo add-apt-repository ppa:chris-lea/node.js
    $ sudo apt-get update
    $ sudo apt-get install nodejs npm
    


    Если есть желание — можно запустить консоль node и поиграться с интерпретатором javascript.

    1.1.3 Среда разработки



    Тут каждый волен выбирать по своему вкусу, лично меня вполне устраивает gedit с установленным набором плагинов gmate. Вполне подходят Netbeans или Webstorm.

    1.1.4 Express и первое приложение



    Теперь пришло время познакомиться с фреймворком express. Фреймворк очень простой, и вполне приемлемо документированный.

    Устанавливаем express глобально:

    $ sudo npm install -g express
    


    Создаем директорию для наших учебных проектов:

    $ mkdir -p ~/projects/node-tutorial
    $ cd ~/projects/node-tutorial
    


    Создаем проект и устанавливаем зависимости:

    $ express first-app
    $ cd first-app && npm install
    


    Желающие могут покопаться в том что нам сгенерировал генератор приложений, думаю, что люди знакомые с javascript могут предположить что там происходит.

    Теперь приложение можно запустить:

    $ node app
    


    И увидеть результат работы http://localhost:3000/

    1.2 Система контроля версий



    Теперь, когда у нас уже есть рабочее приложение, более подробно коснемся работы с сисемой контроля версий. Для того чтобы лучше познакомиться с работой git, стоит почитать книжку Pro Git, но можно и обойтись инструкциями в данном учебнике.

    1.2.1 Настройка git



    Для более комфортной работы с git стоит сначала указать свои личные данные:

    $ git config --global user.name "Your Name"
    $ git config --global user.email your.email@example.com
    


    И настроить алиасы для наиболее часто используемых комманд:

    $ git config --global alias.co checkout
    $ git config --global alias.ci commit
    


    1.2.2 Работа с git



    Git настроен и можно размещать наше приложение в репозитории, инициализируем новый репозиторий:

    $ git init
    


    Добавляем директорию с зависимостями приложения в gitignore:

    $ echo 'node_modules' > .gitignore
    


    Помещаем все файлы в индекс и создаем первый коммит:

    $ git add .
    $ git ci -m "Initial commit"
    


    1.2.3 GitHub



    После размещения кода проекта в репозитории пришло время выложить проект на GitHub. GitHub — это социальная сеть и хостинг для проектов. Огромное число opensource проектов хостится на гитхабе, так что если вы там еще не зарегистрированы — самое время сделать это.

    Перед тем как работать с GitHub нужно будет создать RSA ключи для доступа по ssh. Процедура описана тут. Для пользователей linux привожу инструкцию по созданию ключей если их у вас еще нет.

    $ ssh-keygen -t rsa -C "your_email@youremail.com"
    


    Отвечаем на вопросы генератора, после чего копируем содержимое файла ~/.ssh/id_rsa.pub:

    $ sudo apt-get install xclip
    $ xclip -sel clip < ~/.ssh/id_rsa.pub
    


    После этого нужно пройти по ссылке Account Settings, зайти в раздел SSH Keys и нажать кнопку Add SSH Key и вставить ключ из буфера обмена в поле Key. Затем сохранить.

    Проверить что ключ работает можно так:

    $ ssh -T git@github.com
    


    Возможно вы увидете предупреждение:

    The authenticity of host 'github.com (207.97.227.239)' can't be established.
    # RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
    # Are you sure you want to continue connecting (yes/no)?
    


    Нужно просто ответить 'yes' и тогда, если ключ успешно добавился, вы увидите ответ сервера:

    Hi username! You've successfully authenticated, but GitHub does not
    # provide shell access.
    


    Когда ключи настроены создаем новый репозиторий с названием first-app и дефолтными настройками, после чего выкладываем код на гитхаб:

    $ git remote add origin git@github.com:ваш_ник_на_гитхабе/first-app.git
    $ git push -u origin master
    


    1.3 Разворачиваем приложение



    Теперь наступает самый волнующий этап, мы будем разворачивать приложение на хостинге.
    Для этого воспользуемся услугами облачной системы деплоя Heroku. Если вам интересно как работает хостинг Heroku, советую поизучать их раздел How it Works

    1.3.1 Настройка Heroku



    Для начала нам надо зарегистрироваться и установить необходимый инструментарий.

    Пользователи ubuntu выполняют:

    $ wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh
    


    Когда установка завершится, нужно будет залогиниться из коммандной строки:

    $ heroku login
    


    1.3.2 Размещаем приложение на heroku



    Теперь наше окружение полностью готов к выкладке на хостинг. Размещение node.js проекта на Heroku требует еще нескольких действий, вы можете почитать об этом в документации или просто выполнить инструкции.

    В файле package.json нашего проекта, нужно указать версии ноды и npm, package.json должен выглядеть так:

    {
      "name": "application-name",
      "version": "0.0.1",
      "private": true,
      "scripts": {
        "start": "node app"
      },
      "dependencies": {
        "express": "3.0.x",
        "jade": "*"
      },
      "engines": {
        "node": "0.8.x",
        "npm": "1.1.x"
      }
    }
    


    Теперь в корне проекта создаем файл Procfile:

    $ echo 'web: node app.js'  > Procfile
    


    Проверяем что все запускается с помощью менеджера процессов:

    $ foreman start
    


    Приложение должно быть доступно на http://localhost:5000/

    Добавляем файлы в репозиторий:

    $ git add .
    $ git ci -m "Added Procfile and engines"
    $ git push
    


    Создаем приложение на heroku:

    $ heroku create
    $ heroku keys:add ~/.ssh/id_rsa.pub
    $ git push heroku master
    


    Набираем:

    $ heroku open
    


    и любуемся задеплоеным приложением.
    Поделиться публикацией

    Похожие публикации

    Комментарии 64
      +5
      Труд хороший и полезный, продолжайте обязательно! Но как по мне, изучение технологии лучше начинать не с использования готового фреймворка, а с самых основ, примерно как в замечательной бесплатной книге The Node Beginner Book: в ней последовательно раскрывается и асинхронная модель, основы создания модульной системы, роутинг и много чего ещё. Причём, если честно писать или хотя бы копипастить все примеры, то в конце получится хоть и примитивное, но вполне функциональное веб-приложение, и становится понятнее, как работает тот же express.
        +2
        А мне кажется, что как-раз такого подхода не хватает многим учебным материалам. Сначала заинтересовать, показать какой-то результат, удивить. И если человеку действительно интересно, то он уже обязательно и механизмы работы узучит, и в коде покавыряется.
          +4
          Если бы речь шла об «академическом» подходе «изучить матчасть -> детально изучить технологию -> написать первую строчку кода» — то да, разумеется, это скучно. Но я имею в виду совсем не это, потому что можно начать с банального helloworld и постепенно дописывать различные функции, попутно разбираясь в механизмах работы. Разве это не самое интересное — не просто быстро получить результат, а понимать, почему всё делается именно так?
        +1
        Мне кажется, что как-то уж очень поверхностно.
        То, что вы написали за 10 минут гуглится без особых проблем.

        Создается впечатление, что хотелось сделать сразу много и быстро. Получилось по верхам.
        Я бы порекомендовал более подробно пройтись по подключаемым модулям, обработке запроса и т.д.
          +3
          image
            +1
            Жаль котят конечно, но свежих версий в репозиториях к сожалению нет, заморачиваться с описанием сборки пакетов под разные системы не вижу смысла.
              0
              В статье вы как бы намекаете, что у вас apt-based дистрибутив, сборка пакетов из сорцев там у всех одинаковая почти. В случае с убунтой я уверен на 99%, что есть ppa, куда были выложены пакеты в тот же день, что и релиз.
                0
                Да, я пробовал ставить из неофициальных ппа, но кажется что-то не очень гладко тогда прошло, уже не помню. Наверно стоит попробовать еще раз, если все хорошо пройдет, уберу make install из текста.
                  0
                  или покажите как собрать deb пакет из сорцев.
                    +3
                    github.com/joyent/node/wiki/Installing-Node.js-via-package-manager
                    Тут есть все для установки под убунту:
                    — описана установка через ppa, в последнее время реально обновляемый с задержкой менее суток,
                    — и ссылка на apptob.org/, который полезен для собственноручной сборки и установки через .deb.
                  0
                  sudo add-apt-repository ppa:chris-lea/node.js
              • НЛО прилетело и опубликовало эту надпись здесь
                  0
                  node ставится в /usr, но никто не мешает указать --prefix. Про переменные можно поподробнее, возникли какие-то проблемы при прохождении? Вроде бы последние версии аккуратно встают в path и ничего не требуют, но я наверно все шаги перепроверю на виртуалбоксе.
                  +3
                  Вы слишком перемудрили с примером c «Hello world» примером.
                  Намного проще и понятней пример из репозитория:
                  var express = require('express');
                  var app = express();
                  
                  app.get('/', function(req, res){
                    res.send('Hello World');
                  });
                  
                  app.listen(3000);
                  console.log('Express started on port 3000');
                  

                  Там же можно посмотреть еще 10ток примеров работы с express
                    0
                    Этот пример генерируется автоматически с помощью генератора express, пока что код не писали вообще. Это скажем так «ознакомительная» глава.
                    0
                    Соглашусь, что слишком поверхностно. С одной стороны, такой подход тоже неплох — ссылки указаны, изучай каждую из технологий по мере появления в тексте статьи, потом возвращайся к статье и т.д. В конце получится, что знаком уже с несколькими технологиями сразу. Но с другой стороны, статья в этом случае должна называться не «Web-разработка на node.js и express. Изучаем node.js на практике», а «Основные вещи, которые должен знать каждый, кто хочет писать на node.js и express». Практики тут очень мало. Но в целом, даже за подборку ссылок стоит сказать спасибо.
                      +1
                      Я потихоньку начинаю бояться, что все будут считать node.js == express.
                      Я конечно люблю работы TJ, и сам пользуюсь, но, мне кажется, что все таки надо разграничивать node,js и пакеты для него. А то может складываться неправильное представление о node.js
                        0
                        Я не думаю, что это так уж страшно, в конце концов ассоциация между ruby и rails еще сильнее и ничего ужасного в этом нет
                          +2
                          А я больше боюсь, что Node === Socket.IO.

                          Вообще взлет Express — дело ожидаемое. Львиное большинство же хочет писать веб-приложения, вот и хватаются за веб-фреймворк. Особенно это касается тех, кто приходит из Руби — они ищут аналог Рельсов и начинают клепать поверх Express те же велосипеды, что и под Rails или Sinatra.

                          Вообще, это еще хорошо, что Express — это только 70% всей node-разработки, а не 90, как в случае с Rails и Ruby*. Все-таки, асинхронный стиль программирования отпугивает довольно много традиционных веб-разработчиков.

                          *Цифры с потолка, но идея понятна.
                          +1
                          Я все понимаю, но:
                          1) Вашу мать, сколько можно вы про ключи написали хоть что-то?
                          2) Какой Jade? (В б еще туда всунули coffe). Это должен быть простой пример.
                          3) Скажите как, вы додумались до такого?

                          http.createServer(app).listen(app.get('port'), function(){
                            console.log("Express server listening on port " + app.get('port'));
                          });
                          


                          это express!!!

                          var app = express.createServer(express.logger());
                          ....
                          var port = 8000;
                          app.listen(port, function() {
                            console.log("Listening on " + port);
                          });
                          


                          Лирика закончена теперь по сути, плохой пример. Heroku плохая платформа, файловая система readonly, чтобы использовать redis или что то еще, еще нужно верифицировать аккаунт с данными о кредитной карте. Очень высокая нагрузка на сервера Heroku дают иногда о себе знать. Кроме этого там квота всего 100мб хотя реальных 97мб. Лучше или nodester или свой.
                            0
                            1) Я имею ввиду как их правильно сделать, как в Linux так и в Windows.
                            2) Лучше ejs проще да и нагляднее.
                            3) Вас уже отругали выше…
                              0
                              1. Мм… действительно стоит объяснять как сделать ключи? Ссылки не достаточно? Я подумаю.
                              2. Дело вкуса, мне нравится jade.
                              3. ок повторюсь, этот исходник сгенерирован автоматически, так же как и то что генерит например rails new. Цель первой главы познакомиться с технологиями и получить первый работающий пример без написания какого либо кода.
                                0
                                1. Объяснять не надо, просто команду приведите. make install же без объяснений привели и даже без ссылки :)
                                  +1
                                  да, согласен, на этом месте многие застрять могут
                                0
                                Jade лаконичен, понравится тем кто использовал haml на тех же рельсах. Путь html (ejs) => haml (jade) — это путь в один конец. Попишешь на последнем и возвращаться не захочется.
                                Писать закрывающие теги? Нет, не слышал.
                                  0
                                  А мне кажется вполне в два.
                                +1
                                >чтобы использовать redis или что то еще, еще нужно верифицировать аккаунт с данными о кредитной карте
                                Никаких проблем с redis на heroku у меня не было, redistogo подключаешь и вперед.
                                данные о кредитке я тоже не оставлял, правда я давно регистрировался, они что-то изменили?
                                  0
                                  Да уже поменяли, ввели верификацию.
                                +2
                                Продолжайте!

                                Ннадеюсь, heroku лишь для быстрого старта приведён, а в дальнейшем будет описано как самому развернуть production на голом debian/ubuntu server по ssh.
                                  +1
                                  Я думаю что основная часть будет ориентирована на деплой в heroku, хотя развертывание прода на голом сервере, тоже интересная тема, можно и запланировать такую главу
                                    +2
                                    Имхо, нужно. Часто на этом затыкаются попытки освоить новый стэк. VDSки дешёвы, но новые технологии из коробки не работают, а оплачивать хостинг типа heroku не каждый заказчик захочет.
                                  +1
                                  Добавлю для тех, кто пользуется Windows.

                                  Git:

                                  — Классика жанра — Git for Windows. Можно смело выбирать тот пакет, что слева: для пользователя Git его хватит за глаза.
                                  — Более дружелюбное и современное — GitHub for Windows. Сам не пробовал, но разработчики из GitHub нахваливают этот клиент и утверждают, что он вполне может работать и с другими кодохостингами, в первую очередь с BitBucket, Google Code и CodePlex (и даже показывает граватары для коммитеров).

                                  Node.JS:

                                  — опять же, без необходимости сборки из исходников Node доступен на nodejs.org/#download После установки и node, и npm могут работать из обычной командной строки.

                                  — альтернативный вариант — WebMatrix 2. Из коробки умеет запускать приложения под облегченным IIS в один клик, редактор подсвечивает синтаксис, неплохо автокомплитит, понимает LESS, SASS, CoffeeScript а также имеет два примера проектов с express в качестве стартовых темплейтов. Тут же присутствует интеграция с Windows Azure (облаком от Микрософт).

                                  Замечу также, что одно другому не мешает: у меня на рабочей машине и обычный node, и встроенный в WebMatrix уживаются без каких-либо проблем. Даже если вы не хотите пользоваться IIS, WebMatrix будет для вас очень неплохой и, самое главное, бесплатной альтернативой для WebStorm или SublimeText.
                                    0
                                    Я тоже замечу: вы так подробно описывали, что к чему и зачем в самом начале статьи, а потом просто у помянули хероку. Ни пары строчек о том, что это и зачем надо.

                                    И да, деплой проекта на свой сервер — важная часть, которую нельзя выбрасывать из туториала. Раз вы уже начали.
                                      –1
                                      Кстати да — по поводу развёртывания проекта есть вопрос: какими средствами удобнее пользоваться для поддержания проекта в запущенном состоянии?
                                      Скажем, cron, который дёргает localhost:3000/ раз в минуту, и перезапускает проект в случае если тот не откликается — по-моему ужасный костыль.
                                        +1
                                        Может для ноды есть отдельные решения, но так нравится Monit
                                          0
                                          Лично я использую supervisord.
                                          Я же правильно понимаю, что Monit требует обязательного использования pid-файлов. Как Вы его готовите? :)
                                            +1
                                            Нет, необязательно. Тестов всяких много, чаще всего использую запросы прямо к демонам (порты или юникс/сокеты). HTTP, MySQL, Memcache, SSH знает из коробки (список большой это то, что использую), если что-то не знает, то можно просто последовательность байт послать и сравнить ответ с ожидаемым.
                                            0
                                            С Forever всплыли такие проблемы:
                                            1) Нужно было самому писать init.d-скрипты для запуска вместе с системой.
                                            2) Контроль запущенных процессов отказывался работать. Было лень разбираться, почему.

                                            Так что отказался от него в пользу supervisord.
                                          –2
                                          Мне кажется, что все подобные решения ужасные костыли. Мониторинг конечно нужен, чтобы узнать о падении. Первые версии node.js страдали от утечек и падений, но, к счастью, это уже в прошлом. Сейчас в 99.9% случаев падение происходит из-за своей ошибки. И правильный подход будет — вычислить её и исправить. Разного рода «перезапускатели» создают вокруг node.js атмосферу ненадёжности, хотя это не так. Я публиковал цикл статей о разработке сайта на node.js. Так вот, он работает месяцами и я перезапускаю его, только когда нужно что-то обновить. Падения были в самом начале, но из за ошибок в самом приложении и все они были исправлены.
                                            0
                                            Сейчас в 99.9% случаев падение происходит из-за своей ошибки. И правильный подход будет — вычислить её и исправить.

                                            Ну разумеется :) Просто не хочется быть разбуженным посреди ночи звонком из-за упавшего сайта. Лучше утром следующего дня увидеть что сервер падал, глянуть лог и исправить ошибку.

                                            Разного рода «перезапускатели» создают вокруг node.js атмосферу ненадёжности, хотя это не так.

                                            Да к чёрту атмосферу — те кто написал что-то более-менее серьёзное на ноде, понимает что из себя представляет этот инструмент, и это зачастую положительная оценка.
                                            Тот же популярный apache тоже временами падает, и ничего.

                                            В общем, я конечно же за правильный код, но перестраховаться не помешает.
                                            +1
                                            Я использую Runit для этого. Удобно и просто. Хотя по хорошему в node уже появился cluster и демонизировать можно и наверное нужно через него, но пока лень.
                                          0
                                          А под node.js ещё не написано какое-нибудь подобие rvm под ruby?
                                          +2
                                          Для установки лучше всего использовать nvm. Преимущество — легко переключаться между установленными версиями.

                                          Для размещения лучше www.dotcloud.com/ У них два бесплатных инстанса, т.е. можно еще и с базой данных бесплатно поработать. Если интересно, могу описать деплоймент подробнее.
                                            +1
                                            Слушайте, конечно интересно. Информацию новички вынуждены собирать в интернетах по крупицам.
                                            0
                                            Очень правильный подход. В начале обучения всегда хотелось начать писать что-то более менее рабочее. Тонкость только в том, что не зная языка и среды, тяжело делать отладку, что тоже отбивает энтузиазм. А после винды make install сам по себе вызывал у меня ужас и панику. Поэтому посоветую добавлять в конце уроков более академичную информацию: всегда легче скопировать, а потом разобраться как работает, чем усвоить в голове как что-то работает, а потом это воплощать.

                                            Желаю успехов!
                                              0
                                              Расскажите, пожалуйста, чуть подробнее про package.json.
                                              Его смысл я понимаю, но практическая ценность не совсем ясна: в частности, вместо того чтобы указывать в нём версии и зависимости, я, подключаю модули не из node_modules, а из папки ./lib своего проекта (конечно же, предварительно положив туда их исходные коды).
                                              Таким образом при развёртывании проекта на сервере я не забочусь об установленных пакетах и их версиях, а просто копирую всю папку проекта со своей машины — и потому получаю стопроцентно работающий проект.
                                              Какие плюсы-минусы и такого подхода?
                                                0
                                                конечно же, предварительно положив туда их исходные коды

                                                Ненужный этап. Обновление также проще.
                                                  0
                                                  Да, но при таком подходе не столкнусь с проблемой, что на боевом сервере программа будет работать некорректно из-за того что на локальной машине (на которой всё тестировалось), другие версии модулей.
                                                  Обновил lib локально, проверил, работает? — закидывай на сервер и будь уверен в работоспособности.
                                                    0
                                                    Версии модулей у вас зафиксированы :)
                                                      0
                                                      закидывай на сервер и будь уверен в работоспособности
                                                      Точно нак — для этого и создан package.json. Он очень гибкий. Ознакомьтесь с ним поподробнее.
                                                    0
                                                    Смысл приблизительно такой: та софтина, которая разворачивает проект на сервере, вызовет npm install project.json и в вашем инстансе будут установлены именно те модули (и те версии) которые вы указали. Кроме того, для heroku все равно нужен package.json, чтобы указать версию самого node.js.

                                                    Минус вашего подхода — нужно таскать в проекте кучу лишних исходников. Плюс — в системе не будут появляться всякие лишние команды (типа того же автогенератора express).
                                                    0
                                                    Простите, я честно признаюсь, что только начал читать статью и возможно дальше все поясняется (в таком случае заранее прошу прощения), но если вы используете Ubuntu 12.04 (как и я), то зачем собирать node из исходников, если можно установить его так:
                                                    sudo apt-get install nodejs
                                                    
                                                      0
                                                      0.6.12 vs 0.8.1?
                                                        0
                                                        Да, это я заметил. А подскажите, велика ли разница между этими двумя версиями для написания учебного приложения (потому что я в ноде пока что ноль)? Или експрес-фремворк не стартонет без актуальной версии ноды?
                                                          +1
                                                          упс, ниже ответил
                                                            0
                                                            0.8 вышел совсем недавно, так что пока все заточено под 0.6. И, как мне кажется, разработчики не будут ломать совместимость с ним в ближайшее время.
                                                            0
                                                            0.8.1 легко ставится из ppa:chris-lea/node.js
                                                          +1
                                                          на 0.6.12 стартанет, но например в 10.10 если не ошибаюсь версия 0.4.х, а там уже траблы будут. Хотя это скорее всего просто привычка с того времени, когда все очень быстро менялось и было по большей части нестабильно. Я думаю, что перепишу эту часть и опишу установку последней версии из ppa, в таком случае я думаю ни у кого не будет претензий :)
                                                            0
                                                            Понятно, спасибо за ответ!
                                                              0
                                                              С нетерпением жду продлжения статьи.

                                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                            Самое читаемое