
Данный урок, продолжение серии уроков по derbyjs — реактивному фуллстек фреймворку. Читать предыдущие уроки обязательно (первый, второй).
Этот урок будет состоять из двух частей: первая — обзор серверной части дерби-приложения, и вторая — использование модуля
derby-auth (обертки над passportjs) для регистрации/авторизации в дерби-приложении с использованием в том числе и социальных сетей.Часть 1 — серверный код дерби-приложения
Подготовка
В предыдущих примерах для облегчения восприятия информации в качестве серверной части мы использова��и
derby-starter. Этот модуль скрывал от нас подробности серверной настройки. Напомню, что на сервере, derby-приложение строится поверх стандартного expressjs-приложения, в частности там происходит настройка базы данных, подключение нужных нам express-овских middlware. Для нашего сегодняшнего примера необходимо более глубоко разобраться с серверной частью — derby-starter мы использовать не будем.Если кто-то не знаком с
expressjs либо плохо знает nodejs — советую замечательный курс Ильи Кантора.Базовое приложение
В качестве базы для нашего сегодняшнего примера необходимо скопировать мой репозиторий
derby-boilerplate. По сути — это минимальное приложение, аналогичное тому, что было у нас в первом примере, но серверная часть теперь не в derby-starter-е, а в самом проекте (так же здесь используется последний 4-й экспресс, и вообще не используется redis — теперь можно и без него). Копируем командой:git clone https://github.com/derbyparty/derby-boilerplate.git
Давайте исследуем структуру проекта — она уже вполне боевая в отличии от того, что было в предыдущих примерах:
src/
app/
server/
styles/
views/
index.js
В папке
app будет наше дерби-приложение. Схематично, в папке server будет находится «аналог» derby-starter-а (я действительно взял derby-starter, скопировал его туда и чуть причесал), его содержимое мы разберем подробней.В папках
styles и views — стили и шаблоны соответственно. Файл index.js аналогичен тому, что было в предыдущих примерах — несколько строк, запускающих все это дело.Серверная часть derby-приложения
Прежде всего еще раз обращу ваше внимание на то, что дерби работает поверх
expressjs-приложения, поэтому если вы не знаете, что это такое, что такое express-middleware, как они работают, как работают сессии в экспрессе, роутинг — здесь вам нужно остановиться и устранить недостатки в знаниях.Посмотрим, что у нас в папке server:
error/
index.js
server.js
В папочку
error я вынес обработчик ошибок, его я здесь не буду. Скажу только, что это expressjs-middlware, которое срабатывает, когда для набранного url нет обработчика, либо в процессе работы приложения произошли ошибки. Если хотите разберитесь сами.Основное предназначение файла
index.js — подцепить express (который настраивается в файле server.js) и поднять сервер, по указанному в настройках порту:Исходный код (практически без изменений взят из derby-starter)
var derby = require('derby');
exports.run = function (app, options, cb) {
options = options || {};
var port = options.port || process.env.PORT || 3000;
derby.run(createServer);
function createServer() {
if (typeof app === 'string') app = require(app);
var expressApp = require('./server.js').setup(app, options);
var server = require('http').createServer(expressApp);
server.listen(port, function (err) {
console.log('%d listening. Go to: http://localhost:%d/', process.pid, port);
cb && cb(err);
});
}
}
Дальше идет самое интересное, настройка expressjs, с добавленным
derby. Разбор этого момента — ключ к пониманию того, как вообще работает дерби.Я использую 4-й экспресс, у него есть несколько отличий от 3-го. Основное — то, что стандартный middlware теперь отсутствуют в дистрибутиве
express и их необходимо устанавливать отдельно.Вот так, примерно выглядел бы этот файл без дерби:
Код server.js, если бы мы делали обычное expressjs-приложение:
var express = require('express');
// В 4-ом экспрессе все middleware вынесены в отдельные модули
// приходится каждый из них подключать по отдельности
var session = require('express-session');
// Сессии будем хранить в монге
var MongoStore = require('connect-mongo')(session);
// Обработчик ошибок - я вынес его в отдельную папочку,
// чтобы не отвекал
var midError = require('./error');
var MongoClient = require('mongodb').MongoClient;
exports.setup = function setup(app, options) {
var mongoUrl = process.env.MONGO_URL || process.env.MONGOHQ_URL || 'mongodb://localhost:27017/derby-app';
// Инициализируем подкючение к БД
MongoClient.connect(mongoUrl);
var expressApp = express()
if (options && options.static) {
expressApp.use(require('serve-static')(options.static));
}
expressApp.use(require('cookie-parser')());
expressApp.use(session({
secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE',
store: new MongoStore({url: mongoUrl})
}));
// Если бы у на были обычные экспрессовские роуты - мы бы положили их СЮДА
// Маршрут по умолчанию - генерируем 404 ошибку
expressApp.all('*', function(req, res, next) { next('404: ' + req.url); });
// Обработчик ошибок
expressApp.use(midError());
return expressApp;
}
В двух словах напомню, как это работает. Сначала подключаются модули, потом идет самое важное — подключение экспрессовских
middleware (через expressApp.use). По сути эти middlware — это простые функции, которые будут в том же порядке, как зарегистрированы, вызываться для каждого запроса, пришедшего на сервер. Каждый ��з этих middleware может либо ответить на запрос, завершив цепочку обработки (оставшимся middlware управление передано не будет), либо произвести какие-то промежуточные действия с запросом (например, распарсить cookies, по кукам определить сессию и т.д.) и передать управление дальше по цепочке. Порядок подключения middlware очень важен — именно в этой последовательности функции будут вызываться для каждого запроса.А вот так выглядит дерби-вариант — комментированный код server.js с дерби
// 4-ый экспресс
var express = require('express');
// В 4-ом экспрессе все middleware вынесены в отдельные модули
// приходится каждый из них подключать по отдельности
var session = require('express-session');
// Сессии будем хранить в монге
var MongoStore = require('connect-mongo')(session);
// Обработчик ошибок - я вынес его в отдельную папочку,
// чтобы не отвекал
var midError = require('./error');
var derby = require('derby');
// BrowserChannel - аналог socket.io от Гугла - транспорт, используемый
// дерби, для передачи данных из браузеров на сервер
// liveDbMongo - драйвер монги для дерби - умеет реактивно обновлять данные
var racerBrowserChannel = require('racer-browserchannel');
var liveDbMongo = require('livedb-mongo');
// Подключаем механизм создания бандлов browserify
derby.use(require('racer-bundle'));
exports.setup = function setup(app, options) {
var mongoUrl = process.env.MONGO_URL || process.env.MONGOHQ_URL || 'mongodb://localhost:27017/derby-app';
// Инициализируем подкючение к БД (здесь же обычно подключается еще и redis)
var store = derby.createStore({
db: liveDbMongo(mongoUrl + '?auto_reconnect', {safe: true})
});
var expressApp = express()
// Здесь приложение отдает свой "бандл"
// (т.е. здесь обрабатываются запросы к /derby/...)
expressApp.use(app.scripts(store));
if (options && options.static) {
expressApp.use(require('serve-static')(options.static));
}
// Здесь в бандл добавляется клиетский скрипт browserchannel,
// и возвращается middleware, обрабатывающее клиентские сообщения
// (browserchannel основан на longpooling - т.е. здесь обрабатываются
// запросы к адресу /channel)
expressApp.use(racerBrowserChannel(store));
// В req добавляется метод getModel, позволяющий обычным
// express-овским котроллерам читать и писать в БД
// см. createUserId
expressApp.use(store.modelMiddleware());
expressApp.use(require('cookie-parser')());
expressApp.use(session({
secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE',
store: new MongoStore({url: mongoUrl})
}));
expressApp.use(createUserId);
// Здесь регистрируем контроллеры дерби-приложения,
// они будут срабатывать, когда пользователь будет брать страницы
// с сервера
expressApp.use(app.router());
// Если бы у на были обычные экспрессовские роуты - мы бы положили их
// СЮДА
// Маршрут по умолчанию - генерируем 404 ошибку
expressApp.all('*', function(req, res, next) { next('404: ' + req.url); });
// Обработчик ошибок
expressApp.use(midError());
return expressApp;
}
// Пробрасываем id-юзера из сессии в модель дерби,
// если в сессии id нет - генерим случайное
function createUserId(req, res, next) {
var model = req.getModel();
var userId = req.session.userId;
if (!userId) userId = req.session.userId = model.id();
model.set('_session.userId', userId);
next();
}
Потратьте определенное время, чтобы понять все это. По сути основное здесь то, что используется
browserify — он нужен для того, чтобы отдавать клиенту в браузер так называемый «бандл» — блок данных, содержащий все javascript-файлы дерби-приложения, а также шаблоны (css-скрипты здесь содержаться не будут). Нужно понимать, что при обычном запросе какой-нибудь страницы сайта клиентом — в браузер сразу бандл не отдается, это было бы слишком накладно. Отдается уже отрендеренная страница со стилями, и с какими-то первоначальными данными. Это логично ведь здесь очень важна скорость. Далее уже сама эта страница подгружает «бандл» — делается запрос по адресу "/derby/:bandle-name".Я уже говорил, что одной серверной части дерби может соответствовать несколько «дерби-приложений» — по сути для каждого из них будет свой бандл. Это логично. Так мы, например, можем отделить клиентскую часть сайта от админки — чтобы не передавать лишние данные всем подряд. Если так, то нужно будет прописать:
expressApp.use(clientApp.scripts(store));
expressApp.use(adminApp.scripts(store));
То же касается и обработки роутов. Если у на будет 2 «дерби-приложения», мы получим вместо:
expressApp.use(app.router());
��одключение двух роутеров приложений:
expressApp.use(clientApp.router());
expressApp.use(adminApp.router());
Ну и еще вы должны понимать то, что кроме обработчиков роутов «дерби-приложений» мы здесь вполне можем добавить своих
expressjs обработчиков, организуя любой, необходимый нам RESTFull API, как говорится, с блекджеком и барышнями. Это все работает благодаря тому, что клиентский роутер дерби-приложений, не находя нужного обработчика у себя в приложении, просто отправляет запрос для обработки на сервер.Следующим важным моментом является подключение
browserchannel — это гугловский аналог socket.io. По сути это транспорт, благодаря которому наше приложение в реальном времени будет синхронизировать данные с сервером. Под капотом находится то, что в определенный момент клиентский скрипт browserchannel добавляется в бандл, а так же обработка запросов от этого модуля (он основан на longpooling — поэтому требуется обработка, в данном случае по адресу /channel)Да, хотел бы отметить, что в моем примере базовой настройки дерби не используется
redis. Недавно вышло обновление дерби, и теперь, redis не является обязательным (он еще нужен, если мы будем делать горизонтальное масштабирование — то есть для повышения производительности запускать сразу несколько дерби-серверов).Итак, в данный момент вы должны понять, что данная структура позволяет очень гибко добавлять что угодно в наше приложение. Будь то какие-то
expressjs-middlware, плагины дерби, отдельные дерби-приложения, экспресс-обработчики — что угодно. Все это будет спокойно жить вместе.Так, ну и последний момент на который нужно обратить внимание, особенно в свете вопроса авторизации — сессии. Как вы могли заметить сессии в дерби используются вполне себе
express-овские. То есть все происходит по классической схеме: для идентификации используется кука (которая будет храниться в браузере клиента), сами сессии хранятся в mongodb в коллекции sessions.Если заглянуть в функцию
createUserId:function createUserId(req, res, next) {
var model = req.getModel();
var userId = req.session.userId;
if (!userId) userId = req.session.userId = model.id();
model.set('_session.userId', userId);
next();
}
Видно, что в ней из сессии извлекается userId, если же он отсутствует, id генерируется случайным образом (
model.id()) и записывается и в модель по пути _session.userId (эти данн��е будут сериализованы и переданы на клиент) и в саму сессию.Представим себе несколько вариантов запросов:
- Пользователь первый раз зашел на сайт и запросил главную страницу — на сервере будет сформирована новая кука, новая сессия (с новым случайным
userId) — в браузере отрисовывается главная страница и если мы на клиенте заглянем в модель, то по пути '_session.userId' — увидим наш вновь сформированный айдишник. Теперь бродя по страницам нашего приложения (запросов к серверу не будет) — у клиента всегда есть его Id, и если он сделает какие-то запросы к серверу, мы всегда по куке и по сессии сможем его распознать. - Пользователь так и не зарегистрировался и зашел через неделю. Кука у него сохранилась — ему выдастся тот же
userId— все нормально. - Пользователь зашел с другого браузера — то же что и в п. 1
Предположим, как бы мы разработали регистрацию/авторизацию (с точки зрения сервера). Допустим в браузере клиент ввел регистрационные данные — дальше они должны попасть на сервер, где мы бы создали запись в коллекции users, даже id придумывать уже не надо, он уже есть.
При авторизации сложнее, клиент зайдя на сайт получит случайный id, потом зайдет на страницу авторизации — введет логин/пароль, передаст это все на сервер, тут сервер поймет, кто это и должен поменять userId в сессии и в
_session.userId.Что бы мы там хранили в этой коллекции. Скорее всего: username, email, passwordHash, может быть какие-то свои статистические данные. Если на нашем сайте была бы авторизация (регистрация) через соц. сети, то здесь же хранились бы ключи соц.сетей. Еще, конечно, пользовательские настройки — их бы тоже было бы неплохо хранить здесь.
Естественно нам бы захотелось, чтобы часть этой коллекции никто бы не смог просмотреть на клиенте. Сейчас с этим нет проблем. Недавно в дерби появились так называемые проекции (возможность подписываясь на коллекцию, подписываться не на весь документ целиком, а только на определенные его поля), но еще пару недель назад — это было невозможно, и модуль
derby-auth, который мы будем рассматривать, этого еще не умеет. Там используется разделение пользовательских данных на 2 части: первая — коллекция auths (здесь только приватные данные), вторая users — здесь открытые общедоступные данные. Итак, derby-authЧасть 2 — авторизация в derby-приложении (модуль derby-auth)
Немного о derby-auth
На данный момент существует 2 пакета — авторизации для дерби. Это
derby-auth и derby-passport. Обе они являются обертками над passportjs, (одна из них, по-моему, форк другой). Актуальные версии и той и той предназначены для использования с пятой версией дерби (напомню 6-ая еще в альфе), под 6-ую еще не обновили, но это не мешает нам использовать derby-auth во всех своих проектах на 6-ке (недоступны только шаблоны html форм, но у нас они все-равно кастомные).Мой выбор в пользу
derby-auth только потому, что я с ним работал.Еще одно ограничение, о котором я уже упоминал, это то, что пользовательские данные в обоих этих проектах разбиты на две части. Общедоступные данные, должны храниться в коллекции
users, а закрытые в auths. Недавно в дерби появилась возможность делать «проекции» — то-есть подписываться только на определенные поля коллекции — думаю, когда эти модули обновятся до 6-ой версии дерби — ограничение уйдет.update
Через несколько дней после написания данной статьи Владимир Махаев (@vmakhaev) разработал офигенный модуль авторизации derby-login по мотивам derby-auth. Он под 0.6, поддерживает проекции, в нем есть уже готовые компоненты для регистрации, входа, смены пароля — короче, используйте только его.
Кстати, не забудьте поблагодарить Владимира, хотя бы звездочкой на гитхабе. Очень полезную вещь он сделал.
Немного о passportjs
PassportJS — это middleware для авторизации под node.js. Это абстрагирующий модуль, позволяющий вашему приложению использовать как обычную авторизацию логином и паролем, так и авторизацию через практически любой сервис, поддерживающий Oauth, Oauth 2 и т.д. авторизацию. Способы авторизации называются здесь стратегиями. Например, авторизация через логин/пароль называется LocalStrategy («локальной стратегией авторизации»), а автризация через GitHub — GithubStrategy. Каждая стратегия вынесена в отдельный модуль, таким образом подключение всего этого дела к node.js-приложению будет выглядеть примерно так:var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var GithubStrategy = require('passport-github').Strategy;
Здесь же следует отметить особенности использования стратегий авторизации через социальные сети. Если кто-то еще не знает, то прежде, чем наше приложение сможет использовать такую авторизацию, необходимо его (приложение) зарегистрировать в соц. сети и получить два параметра, обчно это
appId (или clientId — некий уникальный id-шник нашего приложения в этой соц. сети) и Secret (секретный ключ, который никому нельзя говорить, он будет использоваться для того, чтобы наше приложение смогло подтвердить, что мы — это мы). При регистрации так же необходимо будет ввести так называемый redirectURL — то-есть url-адрес, куда соц. сеть перенаправит браузер клиента после авторизации. В моем примере это будет: localhost:3000/auth/github/callback — но об этом чуть позже.В приложении-примере, я буду использовать авторизацию через
github, регистрация приложения там очень простая (у других провайдеров не сложнее). Прежде всего нужно иметь обычный пользовательский аккаунт, в его настройках легко создается приложение.
Никому не говорите мой секрет.
Подключаем derby-auth к нашему приложению
Итак, прежде всего ставим
derby-auth напрямую из git-репозитория, почему-то мастер версия у них под предыдущую derby:npm install git://github.com/cray0000/derby-auth#0.5 -S
Derby-auth автоматом ставит passport и passport-local, осталось установить passport-github:npm install passport-github -S
Теперь переходим к настройкам. Все будем делать в соответствии с инструкцией — https://github.com/lefnire/derby-auth/tree/0.5. Большая часть действий по подключению осуществяется на сервере, то есть нам нужен src/server/server.js.
Шаг 1
Подключаем сам derby-auth, и настраиваем стратегии (естественно в нормальном приложении, настройки должны браться из файла-конфига или переменных окружения)
// Подключаем derby-auth
var auth = require('derby-auth');
var strategies = {
github: {
strategy: require("passport-github").Strategy,
conf: {
clientID: 'eeb00e8fa12f5119e5e9',
clientSecret: '61631bdef37fce808334c83f1336320846647115'
}
}
}
Понятно, что если бы стратегий было больше, все они были бы здесь перечислены:
Шаг 2
Устанавливаем опции:
var options = {
passport: {
failureRedirect: '/login',
successRedirect: '/'
},
site: {
domain: 'http://localhost:3000',
name: 'Derby-auth example',
email: 'admin@mysite.com'
},
smtp: {
service: 'Gmail',
user: 'zag2art@gmail.com',
pass: 'blahblahblah'
}
}
В принципе, все понятно из названий:
failureRedirect, successRedirect — url-адресу куда клиент будет перенаправлен в случае не успешной/успешной авторизации соответственно. Информация о сайте нужна для отсылки в стратегии (например, ее могут использовать соц. сети, если соответствующие поля не были заполнены при регистрации приложения). Параметры почты нужны для того, чтобы работала возможность «забыл пароль — сбросьте его и отошлите мне новый на мыло».Шаг 3
Инициализируем хранилище
auth.store(store, false, strategies);
На этом шаге
derby-auth добавляет контроль доступа к данным, пользователь не сможет получить чужие записи из коллекции auths — будет видеть только свою учетку. Второй параметр в данный момент не используется.Шаг 4
Убедимся, что к экспрессу подключены middleware по хранению сессий и
body-parser (у нас его пока нет — установим npm i body-parser -S и подключим): expressApp.use(require('cookie-parser')());
expressApp.use(session({
secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE',
store: new MongoStore({url: mongoUrl})
}));
expressApp.use(require('body-parser')())
// Добавим method-override тоже,
// я обычно его ставлю, чтобы PUT стал доступен
// в контроллерах
expressApp.use(require('method-override')())
Ну и последнее — подключаем
derby-auth middeware — здесь подключается куча всего об этом дальше:expressApp.use(auth.middleware(strategies, options));
Давайте разберемся, что здесь происходит. Если уж это — middleware, логично предположить, что здесь регистрируются какие-то контроллеры
expressjs, обрабатывающие запросы к определенным url. Так оно и есть — вот сводная информация о контроллерах:| Роут | Метод | Параметры | Предназначение |
|---|---|---|---|
| /login | post |
username, password | Служит для входа в систему по логину и паролю |
| /register | post |
username, email, password | Служит для регистрации пользователя по логину и паролю |
| /auth/:provider | get |
отсутствуют | Переходя сюда мы будем перенаправлены на страницу авторизации соответствующей соц. сети. Пример пути: /auth/github |
| /auth/:provider/callback | get |
отсутствуют | Сюда соц. сеть сернет управления после авторизации — derby-auth обработает это и перенаправить нас на failureRedirect или successRedirect url. Пример пу��и: /auth/github/callback — этот url нужен для того, чтобы ввести его в настройках при регистрации приложения в соц. сети. |
| /logout | get |
отсутствуют | Сделав такой запрос мы «выйдем» из аккаунта и будем перенаправлены на '/' |
| /password-reset | post |
На соответствующий email будет отослан новый пароль (запрос должен быть AJAX) | |
| /password-change | post |
uid, oldPassword, newPassword | Смена пароля соответствующего юзера (запрос должен быть AJAX) |
В инструкции есть еще один (необязательный) шаг — подключение компонет (html и стили форм авторизации/регистрации и т.д.), но эти компоненты написаны под дерби 0.5 и еще не обновлены. В своем примере, я следаю простейшую реализацию форм, чтобы продемонстрировать базовые принципы работы. Но прежде необходимо обсудить еще один момент.
Что мы увидем в модели на клиенте
Derby-auth передает определенные данные на клиент, которые позволяют узнать авторизовался ли пользователь, его id, а так же вспомогательную информацию, описывающую проблемы со входом и регистрацией:| Пусть | Описание |
|---|---|
| _session.loggedIn | Булева переменная — авторизован/не авторизован пользователь |
| _session.userId | id пользователя |
| _session.flash.error | Массив строк — вспомогательные сообщения об ошибках (всевозможные ошибки авторизации/регистрации) |
| auth.{userId}.local | Данные по локальной регистрации |
| auth.{userId}.{provider} | Данные по регистрации через соответсвующую соц. сеть |
Все это доступно в шаблонах и будет нами использовано, для определения статуса авторизации.
Пример приложения
Вообще, конечно, если бы визуальные компоненты из
derby-auth подходили бы к 6-ой версии дерби, приложение написалось бы в два счета, без них сложнее. Чтобы упростить разработку, подключим bootstrap, для этого в src/app/index.js вписываемapp.use(require('d-bootstrap'));
предварительно сделав
npm i d-bootstrap -S
Третий бутстрап установлен.
В приложении я сделаю 2 страницы: первая, под адресу "/" со статусом авторизации, чтобы мы могли увидеть вошел ли пользователь в свою учетку, а так же учетные данные, вторая, по адресу '/login' непосредственно с фармами регистрации авторизации, чтобы поэкспериментировать. Так же перед рендерингом каждой из этих двух страниц, я буду подтягивать учетные пользовательские данные из коллекции
auths.Думаю файл дерби-приложения
src/app/index.js не требует особых комментариев, единственное, что мы не обсуждали, это контроллер"*", но это стандартная штука для экспресса:var derby = require('derby');
var app = module.exports = derby.createApp('auth', __filename);
global.app = app;
app.use(require('d-bootstrap'));
app.loadViews (__dirname+'/../../views');
app.loadStyles(__dirname+'/../../styles');
app.get('*', function(page, model, params, next){
var user = 'auths.' + model.get('_session.userId');
model.subscribe(user, function(){
model.ref('_page.user', user);
next();
});
});
app.get('/', function (page, model){
page.render('home');
});
app.get('/login', function (page, model){
page.render('login');
});
По шаблонам поступим так — сделаем
layout c менюшкой в файле index.html, а в файлы home.html и login.html положим, соответственно, страницу с информацией о статусе пользователя (вошел/не вошел, привязан ли github-аккаунт), и страницу с авторизацией/регистрацией.Итак, index.html
<import: src="./home">
<import: src="./login">
<Title:>
Derby-auth example
<Body:>
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">Derby-auth Example</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<view name="nav-link" href="/">Информация</view>
<view name="nav-link" href="/login">Логин</view>
</ul>
</div>
</div>
</div>
{{if _session.flash.error}}
<div class="container">
{{each _session.flash.error as #error}}
<div class="alert alert-warning">{{#error}}</div>
{{/}}
</div>
{{/}}
<view name="{{$render.ns}}"></view>
<nav-link: element="nav-link">
<li class="{{if $render.url === @href}}active{{/}}">
<a href="{{@href}}">{{@content}}</a>
</li>
Эта менюшка и вообще большая часть разметки здесь — чисто вспомогательные и особо не связаны с авторизацией как таковой (ну кроме alert-ов). Единственное, что здесь может вызвать вопросы, это:
<view name="{{$render.ns}}"></view>
Вместо этой строки дерби будет вставлять содержимое либо
home.html, либо login.html, в зависимости от того, что мы прописали в контроллере: page.render('home') или page.render('login')Страничка со вспомогательной информацией,
home.html:<index:>
<div class="container">
<h1>Информация:</h1>
{{if _session.loggedIn}}
<p>Пользователь вошел в систему</p>
<h2>Локальная стратегия:</h2>
{{if _page.user.local}}
<p>Имя пользователя: <b>{{_page.user.local.username}}</b></p>
{{else}}
<p>Данные отсутствуют</p>
{{/}}
<h2>GitHub:</h2>
{{if _page.user.github}}
<p>Имя пользователя: <b>{{_page.user.github.username}}</b></p>
{{else}}
<p>Данные отсутствуют</p>
{{/}}
<a class="btn btn-danger" href="/logout">Выйти</a>
{{else}}
<p>Пользователь НЕ вошел в систему</p>
Старница <a href="/login">Регистрации/Авторизации</a>
{{/}}
</div>
Обратите внимание на кнопку «Выйти» — здесь используется контроллер
GET /logout из derby-authНе будем особо заморачиваться и возьмет форму логина вместе со стилями прямиком с сайта бутстрапа, там есть отличный пример «sign in» http://getbootstrap.com/examples/signin/
Стили тупо скопрированы из бутстраповского примера - файл slyles/index.styl
.form-signin {
max-width: 330px;
padding: 15px;
margin: 0 auto;
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
}
.form-signin .checkbox {
font-weight: normal;
}
.form-signin .form-control {
position: relative;
height: auto;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
Файл login.html с авторизацией и регистрацией:
<index:>
<view name="signin"></view>
<hr/>
<view name="signup"></view>
<signin:>
<div class="container">
<form class="form-signin" role="form" action='/login' method='post'>
<h3 class="form-signin-heading">Вход</h3>
<input name="username" type="text" class="form-control" placeholder="имя" required="" autofocus="">
<input name="password" type="password" class="form-control" placeholder="пароль" required="">
<button class="btn btn-lg btn-primary btn-block" type="submit">Войти</button>
<br/>
<a class="btn btn-lg btn-danger btn-block" href="/auth/github">Через GitHub</a>
</form>
</div>
<signup:>
<div class="container">
<form class="form-signin" role="form" action='/register' method='post'>
<h3 class="form-signin-heading">Регистрация</h3>
<input name="username" type="text" class="form-control" placeholder="имя" required="" autofocus="">
<input name="email" type="email" class="form-control" placeholder="имеил" required="">
<input name="password" type="password" class="form-control" placeholder="пароль" required="">
<button class="btn btn-lg btn-primary btn-block" type="submit">Зарегистрироваться</button>
</form>
</div>
Если запустить все будет выглядеть примерно вот-так:

Бутстаповскую форму я немножко поменял, исходя из наших derby-auth контролленов. На что обращаем внимание в форме регистрации: это post форма ведущая к экшену /login — обрабатывать будет derby-auth, имена полей username и password — все из нашей таблички. Вход через GitHub — get запрос к /auth/github — тоже оттуда.
Форма регистрации. То же самое — post к /register, поля: username, email, password.
Запускаем приложение: npm start — смотрим статус — «пользователь не вошел в систему», регистрируемся, видим — «пользователь в системе». Можем выдти — /logout. Пока не вышли у нас есть возможность привязать к текущей учетке github-аккаунт, для этого нужно кликнуть «войти через github», мы будем перенаправлены на гитхаб, там авторизуемся, все подтвердим и вернемся обратно на сайт.
Derby-auth так же позволяет, не регистрируясь через локальную стратегию, регистрироваться на сайте через аккаунты соц. сетей. Мне больше по душе методика, применяемая на хабре, когда лишь зарегистрированный пользователь может привязать к своей учетке аккаунты соц. сетей, но для этого derby-auth потребуется немножко подправить.
Поиграйтесь с тем, что получислось. Просмотрите доступные данные, используя консоль разработчика (введите app.model.get() в консоли).
Прошу прощения, что мы не реализовали в примере оставшиеся фишки
derby-auth (сброс и изменение пароля) — этот урок и так вышел довольно большим из-за необходимости объяснять серверную часть дерби.Так же мы реализовали упрощенно формы, здесь дело еще и в том, что в полноценном приложении данные блоки будут выносится в отдельные компоненты, а мы их еще не изучали.
Итоговый вариант на github
Прошу тех, кто хоть что-то пробовал сам, отписаться в комментариях и рассказать, что получилось. Не можете писать комментарии на храбре — пишите на мыло. Интересно же, не зря я вообще пишу.
P. S.
Если не хотите пропустить следующие статьи по derbyjs, подписывайтесь на обновления в моем профиле: zag2art. Сам так делаю — на хабре же нет возможности добавить в трекер определенный (очень интересный) хаб, чтобы точно ничего не пропустить.
P.P.S.
Кстати, уже вышла дерби 0.6 alpha 7 — окончательный релиз все ближе и ближе.
Если нравится derbyjs — не сочтите за труд поставить звездочку на github
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Ну так как, продолжать серию?
92.86%Да130
7.14%Нет10
Проголосовали 140 пользователей. Воздержались 17 пользователей.
