Разработка клинет-серверной инфраструктуры на javascript (часть 1 — клиент)

от автора

image

О чем эта статья. Я хочу поделиться опытом разработки мобильного приложения на phonegap. В итоге получился целый програмный комплекс с RESTfull сервером, клиентами, да еще хостится на PaaS. Поэтому я опишу отдельно архитектуру клиентского приложения (html5 single page app, завернутое в phonegap), серверного (nodejs с swagger-node-express + node-orm2), и как разместить все это на openshift PaaS.

Для нетерпеливых:
Страница проекта
Исходный код

Думаю, сайт сразу ляжет от хабраэффекта. Это триальный аккаунт с всего-лишт двумя слотами для ноды. Но об этом в конце.

Хочу сразу уточнить — проект не закончен, но уже стабильно работает, плюс можно показать основные части и архитектурные решения.

Начнем, пожалуй, с самого главного — клиента. Я использовал (ex)twitter bootstrap, я понимаю что шаблон пока не очень — но главное это js логика. Само приложение построено на require.js, хотя я сам не против минимизации всего проекта. Дело в том, что на телефоне файлы быстро будут подгружатся, а для сайта, вообще-то, планируется отдельное приложение в дальнейшем. В качестве javascript фреймворка я выбрал marionette.
Сейчас реализовано два основных модуля: Auth и Conferences.
Отключил автостарт:

this.startWithParent = false; 

И вручную запускаю их после инициализации главного модуля.

require(     [         'css!bootstrap_css',         'bootstrap',         'app/modules/conferences',         'app/modules/auth',     ],     function () {         app.start();     } );    

MyConference.addInitializer(function(options){     mainLayout = new MainLayout;     MyConference.mainView.show(mainLayout);     var headerView = new HeaderView;     headerView.MyConference = MyConference;     mainLayout.header.show(headerView);      MyConference.Conferences.start();     MyConference.Auth.start(); }); 

Auth — регистрация/авторизация. Хочу обратить внимание на социальную авторизацию. Я всегда сам реализую авторизацию и не пользуюсь сторонними агрегаторами, незнаю хорошо это или плохо. Реализовано Google, LinkedIn, Facebook, Twitter, можете просто взять мой код, если вам нужно реализовать у себя что-то похожее. Суть социальной авторизации в том, что я с помощью js на клиенте получаю Api key, а потом передаю его на сервер для проверки. Например facebook:

var afterInit = function(){     var sendAccessToken = function(response){         $.post(             cfg.baseUrl + 'auth.json/facebook',             {FacebookKEY: response.authResponse.accessToken},             function(data, message, xhr){                 process_social_resporce(model, data, xhr);             },             "text"         );     }      FB.getLoginStatus(function(response) {         if(response.status == "not_authorized" || response.status == "unknown"){             FB.login(function(response, a) {                 if (response.authResponse) {                     sendAccessToken(response);                 } else {                     console.log(response, a)                 }             }, {scope:'email'});         }else{             sendAccessToken(response);         }     }); }  window.fbAsyncInit = function() {     FB.init({       appId      : cfg.facebookAppId, // App ID       status     : true, // check login status       cookie     : true, // enable cookies to allow the server to access the session       xfbml      : true  // parse XFBML     });     afterInit(); };  // Load the SDK asynchronously (function(d){      var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];      if (d.getElementById(id)) {return afterInit();}      js = d.createElement('script'); js.id = id; js.async = true;      js.src = "//connect.facebook.net/en_US/all.js";      ref.parentNode.insertBefore(js, ref); }(document)); 

Для постов/лайков мне этот ключ не нужен, только узнать что это за пользователь. Так что, если вам нужно использовать offline доступ, то такой метод может не сработать из-за того, что нужно получать отдельный ключ, который js клиентам не выдают.
Отдельная история с twitter. У него нет браузерной клиентской авторизации, поэтому я реализовал серверную. Открывается окошко, там пользователь авторизируется и потом родительское окно считывает ответ с дочернего. Это может работать не во всех браузерах, так что, скорее всего, придется немного его изменить. Но в android проложении работает нормально.

var childWin = window.open(cfg.baseUrl + 'auth.json/twitter/'+Storage.get('API_KEY'), 'Twitter Auth', "height=640,width=480"); childWin.onunload = function(){     var check = function(){         if(childWin.document){             var body = childWin.document.getElementsByTagName("body")[0];             if(!model.isNew() || body.textContent.length > 0){                 process_social_resporce(model, body.textContent);                 childWin.close();             }else{                 setTimeout(check, 100);             }         }else{             setTimeout(check, 100);         }     }     setTimeout(check, 100); } 

Теперь перейдем к основному модулю — Conferences. Здесь, на самом деле, все очень просто. Описываю контролер з роутами.

var ConferencesController = Marionette.Controller.extend(new function(){     return {         main: function(){             MyConference.mainView.currentView.header.currentView.setHeader('Conferences');              var conferencesCollection = new ConferencesCollection;              var spinnerView = new SpinnerView();             spinnerView.render();             conferencesCollection.fetch({                 error: function(){                     console.log('error');                 },                 success: function(collection){                     var mainView = new MainView;                     mainView.collection = conferencesCollection;                     MyConference.mainView.currentView.content.show(mainView);                     spinnerView.remove();                 }             })         },         conference: function(id){             var conferenceModel = new ConferenceModel;             conferenceModel.set('id', id);             conferenceModel.fetch({                 error: function(){                     var conferenceNotFoundView = new ConferenceNotFoundView;                     MyConference.mainView.currentView.content.show(conferenceNotFoundView);                 },                 success: function(conference){                     var conferenceFullView = new ConferenceFullView;                     conferenceFullView.model = conference;                     MyConference.mainView.currentView.content.show(conferenceFullView);                 }             });         },         streams: function(conference_id){             ShowStreams(                 conference_id,                 function(){                     ShowStream(                         MyConference.                             mainView.                                 currentView.                                     content.                                         currentView.                                             model.                                                 streams.                                                     at(0).                                                         get('id')                     );                 }             );         },         stream: function(id){             ShowStream(id);         }     } });  var MainRouter = Backbone.Marionette.AppRouter.extend({     appRoutes: {         "conference/:id": "conference",         "conferences": "main",         "": "main",         "streams/:conference_id": "streams",         "stream/:id": "stream"     },     controller: new ConferencesController }); 

Как видно, создается обычная бекбоновская модель/коллекция, получаются данные и передается в вьюху. Список конференций это обычный CollectionView. Детальнее остановлюсь для View подробного описания конференции. Поддержка OpenStreetMap и GoogleMaps реализована вручную. Можно, конечно, использовать Leaflet, но я не уверен, что гуглу нравится прямой доступ к их картинкам. Так же там картинка/pdf отображается или ссылка на файл, если есть. И вверху справа ссылка на список докладов.
Если пользователь залогинен, он видит три кнопки, «пойти» на конференцию, в избранное и отказаться. Детальное описание я наследовал не от ItemView, а от Layout, поэтому просто определил блок, куда рендерить эти три кнопки.

regions: {     decision: "#decision" }, 

И в зависимости от статуса пользователя, показываю ту или иную вьюху.

if(MyConference.Auth.getUser().isNew()){     var view = new GuestDecisionView; }else{     var desisionModel = new DecisionModel(view.model.get('decision'));     var view = new LoggedInDecisionView({model: desisionModel});     view.parent = this; } this.decision.show(view); 

Осталось только собрать и запустить проет на телефоне. Если нужно додать платформу выполните «cordova platform add android»

cordova platform add android 

Потом

cordova build android 

Для сборки или

cordova run android 

Чтобы посмотреть у себя на телефоне.

Конечной целью является размещение приложений в Google play, Apple App Store и Windows Store. Но основной моей деятельностью является web, а не мобильная, разработка, поэтому я еще не зарегистрирован как разработчик ни в одном из этих магазинов.

Надеюсь кому-то будет полезна эта статья. Я пытался не сильно ее раздувать, но обратить внимание на все основные моменты. Буду рад критике, пожеланиям, pull request’ам в репозиторий. Из-за того, что материала вышло много, я его разбил на две статьи — клиент и сервер. В следующей статье я опишу создание restfull сервера на nodejs с orm’ом автодокументацией и memcached’ом. И как я деплоил все это на PaaS от RedHat — openshift.

ссылка на оригинал статьи http://habrahabr.ru/post/200192/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *