Crie um blog com a pilha MEAN (parte 1)

Estou postando uma postagem no blog sobre como fazer um blog. Então meta.

Confira o CÓDIGO FONTE

<iframe src = ” https://ghbtns.com/github-btn.html?user=jasonshark&repo=mean-blog&type=star&count=true ” frameborder = “0” scrolling = “0” width = “170px” height = “20px “> </iframe>

endossar

Pegue crackin

Instale o modelo inicial e altere os nomes, exclua a imagem boba do README.

$ git clone git@github.com:jasonshark/mean-starter.git
$ mv mean
-starter mean-blog && cd mean-blog
$ npm install

$ node server

Dica: instale o nodemon $ npm install nodemon -ge você não terá que atualizar a página o tempo todo

Processar login de administrador

Existem várias maneiras de lidar com a autenticação no meanstack. Vamos usar o Passport.js , o servidor expresso e os modelos ejs. O angular será útil quando o modelo já estiver renderizado.

Primeiro defina nossas rotas básicas para as páginas de login e registro. Eles vão em server / routes.js .

app.use('/api', apiRouter); // haven't built any api yet
app
.use('/', router);

// home route
router
.get('/', function(req, res) {
res
.render('index');
});

// admin route
router
.get('/admin', function(req, res) {
res
.render('admin/login');
});

router
.get('/admin/register', function(req, res) {
res
.render('admin/register');
});

Agora vamos configurar as visualizações. Vamos adicionar o mecanismo de modelo ejs e armazenar as visualizações em uma nova pasta chamada visualizações. Primeiro, configuraremos o servidor para renderizar modelos ejs. Adicione ao bloco de configuração expressa server.js :

app.set('view engine', 'ejs');

E adicione ejs como uma dependência para o servidor:

$ npm install ejs --save

Em seguida, crie a pasta de visualizações e os arquivos:
$ mkdir views $ mv public/index.html views/index.ejs $ mkdir views/admin $ touch views/404.ejs views/admin/login.ejs views/admin/register.ejs views/admin/dashboard.ejs

Em views / admin / login.ejs iremos renderizar um formulário de login básico. Isso carregará uma página html completa com um novo <head>e <body>. O corpo do modelo é semelhante a este:

<body>
<div class='container'>
<div class='row'>
<div class='col-sm-4 col-sm-offset-4 text-center'>
<h1>Admin Login</h1>
<form method='post' action='/login'>
<input type='text' placeholder='Email' name='email' ng-model='user.email' class='form-control'>

<input type='password' placeholder='Password' name='password' ng-model='user.password' class='form-control'>
<input type='submit' class='btn btn-primary' value='Login'>
</form>
<a href='/admin/register'>Register a new account</a>
</div>
</div>
</div>
</body>

Certifique-se de incluir CSS de bootstrap e você está certo. O formulário de registro tem a seguinte aparência:

<body>
<div class='container'>
<div class='row'>
<div class='col-sm-4 col-sm-offset-4 text-center'>
<h1>New user registration</h1>
<form
<form method='post' action='/register'>
<input type='text' placeholder='Email' name='email' ng-model='user.email' class='form-control'>

<input type='password' placeholder='Password' name='password' ng-model='user.password' class='form-control'>
<input type='submit' class='btn btn-primary' value='Register'>
</form>
<a href='/admin'>Login with an existing account</a>
</div>
</div>
</div>
</body>

Quase exatamente igual. Agora você pode verificar os formulários básicos navegando para e . A próxima etapa será criar usuários e conectá-los/admin/admin/register

Criar modelo de usuário e configurar passaporte

Então, para ter administradores, vamos criar um modelo de usuário em nosso banco de dados. Crie o modelo em server / models / user.js :

var mongoose = require('mongoose'),
passportLocalMongoose
= require('passport-local-mongoose');

var User = new mongoose.Schema({
email
: {
type
: String,
required
: '{PATH} is required!'
}
});

// Passport-Local-Mongoose will add a username,
// hash and salt field to store the username,
// the hashed password and the salt value

// configure to use email for username field
User.plugin(passportLocalMongoose, { usernameField: 'email' });

module.exports = mongoose.model('User', User);

Para simplificar o gerenciamento de usuários com passport.js, estamos usando passport-local-mongoose , que é uma camada de abstração sobre passport-local . O passaporte local é uma “estratégia de autenticação” no topo do passaporte. Você pode incluir várias estratégias para diferentes provedores. Isso me confundiu por muito tempo. Scotch.io tem uma ótima série de tutoriais sobre autenticação com várias estratégias em um aplicativo expresso. Também tenho um aplicativo de exemplo que implementa login com e-mail / senha, facebook e spotify juntos como um só. Por enquanto, vamos seguir em frente.

continue nadando

Instale as dependências:
$ npm install passport passport-local passport-local-mongoose --save

Inicialize as sessões passport.js em server.js :

var passport = require('passport');
require('./server/passport')(passport); // this file is defined below
app
.use(passport.initialize());
app
.use(passport.session());

Defina as opções de configuração do passaporte em server / passport.js . Peguei isso diretamente do README passport-local-mongoose :

// requires the model with Passport-Local Mongoose plugged in
var User = require('./models/user'),
LocalStrategy = require('passport-local').Strategy;

module.exports = function(passport){
// use static authenticate method of model in LocalStrategy
passport
.use(User.createStrategy());

// use static serialize and deserialize of model for passport session support
passport
.serializeUser(User.serializeUser());
passport
.deserializeUser(User.deserializeUser());
};
Crie rotas para lidar com o login / registro do usuário

Temos nossos formulários que enviam uma solicitação POST simples e antiga. Configure algumas rotas expressas com o middleware mágico passport.js. Middleware in express são funções pelas quais a solicitação passa quando uma rota é atingida. A solicitação atinge a rota (por exemplo, POST para “/ login”) e, em seguida, passa por uma série de funções que assumem o formato . Se a função for chamada, a solicitação continuará descendo na cadeia. O Passport nos fornece um middleware que segue este padrão:function(req, res, next){}next()req, res, next

router.get('/admin/dashboard', isAdmin, function(req, res){
res
.render('admin/dashboard', {user: req.user});
});

router
.post('/register', function(req, res){

// passport-local-mongoose: Convenience method to register a new user instance with a given password. Checks if username is unique
User.register(new User({
email
: req.body.email
}), req.body.password, function(err, user) {
if (err) {
console
.error(err);
return;
}

// log the user in after it is created
passport
.authenticate('local')(req, res, function(){
console
.log('authenticated by passport');
res
.redirect('/admin/dashboard');
});
});
});

router
.post('/login', passport.authenticate('local'), function(req, res){
res
.redirect('/admin/dashboard');
});

app
.use(function(req, res, next){
res
.status(404);

res
.render('404');
return;
});

Certifique-se de ter definido o modelo de usuário mongoose:

var User = require('./models/user')

Você verá que incluí uma isAdminvariável. Esse é um middleware que eu mesmo defini. É desagradável, mas garantirá que mesmo se alguém registrar uma conta, não poderá acessar o painel, a menos que faça login com meu endereço de e-mail.

function isAdmin(req, res, next){
if(req.isAuthenticated() && req.user.email === 'connorleech@gmail.com'){
console
.log('cool you are an admin, carry on your way');
next();
} else {
console
.log('You are not an admin');
res
.redirect('/admin');
}
}

Para aplicativos mais complexos, você pode adicionar funções aos usuários. Você pode adicionar funções emitindo comandos para registros no MongoDB diretamente. Veja como verificar o conteúdo de nosso banco de dados (vá em frente e primeiro, para que você tenha alguns dados para examinar!)/admin/register

Verifique no banco de dados se existem usuários

Vamos verificar nosso banco de dados. Abra um shell mongo ( $ mongoddeve estar em execução).

$ mongo
> show dbs
ghost
-clone 0.078GB
test
0.078GB
> use ghost-clone
switched to db ghost
-clone
> show collections
system
.indexes
users

> db.users.find();

Isso muda em nosso banco de dados e mostra aos usuários. O banco de dados pode ser chamado de “média-starter” se você não mudar o nome do banco de dados de desenvolvimento no servidor / env.js .

Configure o painel de administração

Criamos usuários no banco de dados e os logamos. O que realmente queremos é um painel protegido onde podemos criar, editar e salvar nossas postagens.

Primeiro, vamos configurar o layout e criar um novo aplicativo angular chamado . Este aplicativo irá lidar com tudo o que acontece no modelo dashboard.ejs . Eu configurei um pouco como a plataforma fantasma funciona. Podemos adicionar mais funcionalidades em tutoriais posteriores.ghost-clone.admin

Mudei o diretório para distinguir entre nosso aplicativo angular de administrador e o aplicativo angular inicial:public/

public
|- admin
|- js
|- css
|- templates
|- home
|- js
|- css
|- templates

Crie o aplicativo angular em public / admin / js / app.js :

var adminApp = angular.module('ghost-clone.admin', [
'ui.router',
'btford.markdown'
]);

adminApp
.config(function($stateProvider, $urlRouterProvider){

$urlRouterProvider
.otherwise('/');

$stateProvider

.state('allPosts', {
url
: '/',
templateUrl
: '/admin/templates/allPosts.html'
})
.state('addPost', {
url
: '/addPost',
templateUrl
: '/admin/templates/addPost.html'
})
});

Em seguida, vamos entregar o passaporte de objeto do usuário adicionado no servidor para nossa variável e fornecê-lo ao aplicativo angular.req.useruser

<script src='/admin/js/app.js'></script>
<script type='text/javascript'>
var currentUser = <%- JSON.stringify(user); %>;
adminApp
.run(function($rootScope){
$rootScope
.currentUser = currentUser;
});
</script>

O é como EJS torna variáveis. Nós transformamos isso em string e então passamos para nosso $ rootScope.<%- -%>

Variáveis ​​ejs para angular.js

No corpo, estou vinculado a um modelo de navbar:

<div ng-include='"/admin/templates/nav.html"'></div>.

É importante notar agora que o angular está usando modelos do lado do cliente com ui.router. Eles são separados dos modelos ejs do lado do servidor.

Portanto, agora crie um modelo de navbar em public / admin / templates / nav.html :

<!-- Top navigation -->
<nav class="navbar navbar-default navbar-fixed-top" ng-controller='NavCtrl'>
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Ghost clone</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li ng-class="{ active: isActive('allPosts') }"><a ui-sref="allPosts">All posts</a></li>
<li ng-class="{ active: isActive('addPost') }"><a ui-sref="addPost">New Post</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href>{{currentUser.email}}</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<!-- /Top navigation -->

E também faça um NavCtrlpara esta barra de navegação que alternará a classe com base no $ state ui.router está em:.active

adminApp.controller('NavCtrl', function($scope, $state){
$scope
.active = $state;
$scope
.isActive = function(viewLocation){
var active = (viewLocation === $state.current.name);
return active;
};
})

Abaixo da tag nav.html em dashboard.ejs, inclua um espaço para renderizar nossas visualizações

<div ui-view class='full-height' id='main-content'></div>

Adicione e cole algum CSS em public / admin / css / main.css :

html, body, section, .full-height {
height
: 100%;
}

#pad{
font
- Tagged