SailsJS 0.9 рдореЗрдВ рдкрд╛рд╕рдкреЛрд░реНрдЯ рдПрдХреАрдХрд░рдг

рд╢реБрдн рджреЛрдкрд╣рд░, рд╣рдмреНрд░!

рд╣рд╛рд▓ рд╣реА рдореЗрдВ, рдореИрдВ Sails.js рдореЗрдВ рдПрдХ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рд╕рдорд╕реНрдпрд╛ рдореЗрдВ рднрд╛рдЧ рдЧрдпрд╛ред рдкреНрд░рд╛рд░рдВрдн рдореЗрдВ, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЛ рдХреНрд░рд┐рдкреНрдЯреЛ рдФрд░ req.session рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред рд▓реЗрдХрд┐рди рдореИрдВ рдЪрд╛рд╣рддрд╛ рдерд╛, рдЬрд▓реНрджреА рдпрд╛ рдмрд╛рдж рдореЗрдВ, рд╕рд╛рдорд╛рдЬрд┐рдХ рдиреЗрдЯрд╡рд░реНрдХ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдПред

рдкреНрд░рддреНрдпреЗрдХ рд╕реЛрд╢рд▓ рдиреЗрдЯрд╡рд░реНрдХ рдкрд░ рд╕рдордп рдмрд░реНрдмрд╛рдж рди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП ( рдЬрд┐рд╕рдХрд╛ рдЕрд░реНрде рд╣реИ OAuth рдФрд░ рдХреЙрд▓рдмреИрдХ рд╕реЗ рдирд┐рдкрдЯрдиреЗ рдХреЗ рд▓рд┐рдП ), рджреЛ рдЙрддреНрдХреГрд╖реНрдЯ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдкрд╛рдП рдЧрдП: рдкрд╛рд╕рдкреЛрд░реНрдЯ рдФрд░ рдПрд╡рд░реАрде ред

рдЙрдирдХреЗ рдкреНрд░рд▓реЗрдЦрди рдореЗрдВ рдПрдХ рдЕрдЪреНрдЫреА рддреНрд╡рд░рд┐рдд рд╢реБрд░реБрдЖрдд рд╣реИред рдПрдХ рдЫреЛрдЯреЗ рд╕реЗ рдХреИрд╡рд┐рдПрдЯ рдХреЗ рд╕рд╛рдеред рддреНрд╡рд░рд┐рдд рд╢реБрд░реБрдЖрдд рдХреЛ рдЕрдкрдиреЗ рд╢реБрджреНрдзрддрдо рд░реВрдк рдореЗрдВ рдПрдХреНрд╕рдкреНрд░реЗрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╡рд░реНрдгрд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдкрд╛рд▓ (рдореЛрдЯреЗ рддреМрд░ рдкрд░ рдмреЛрд▓, рдПрдХреНрд╕рдкреНрд░реЗрд╕ рдкрд░ рдПрдХ рдЖрд╡рд░рдг) рд╣реИред рдФрд░ рд╕рднреА рдЕрдиреБрд░реЛрдзреЛрдВ рдХрд┐ рдореИрдВрдиреЗ "Google" рдкреНрд░рд╢реНрди рдХрд╛ рд╡рд┐рд╕реНрддреГрдд рдЬрд╡рд╛рдм рдирд╣реАрдВ рджрд┐рдпрд╛ "рдкрд╛рд╕рдкреЛрд░реНрдЯ рдХреЛ Sails.js рдореЗрдВ рдХреИрд╕реЗ рдПрдХреАрдХреГрдд рдХрд░реЗрдВ"ред

рдпрд╣ рдкреЛрд╕реНрдЯ Sails.js рдореЗрдВ рдкрд╛рд╕рдкреЛрд░реНрдЯ рдХреЛ рдПрдХреАрдХреГрдд рдХрд░рдиреЗ рдкрд░ рдПрдХ рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рд╣реЛрдиреЗ рдХреЗ рдореБрджреНрджреЗ рдХреЛ рдмрдВрдж рдХрд░рдиреЗ рдХрд╛ рдЗрд░рд╛рджрд╛ рд╣реИред

рдкреНрд░рд╕реНрддрд╛рд╡рдирд╛


рдкрд╛рд╕рдкреЛрд░реНрдЯ рдХреНрдпреЛрдВ? рдЗрд╕ рд╕рд╡рд╛рд▓ рдХрд╛ рдПрдХ рд╡рд┐рд╕реНрддреГрдд рдЬрд╡рд╛рдм рд╣реИ , рдЬрд┐рд╕рдиреЗ рдЖрдЦрд┐рд░реА рд╢рдмреНрдж рдкреЗрд╢ рдХрд┐рдпрд╛ред

рдкрд╛рд▓ рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ


рдореИрдВ рд╡рд┐рд╡рд░рдг рдореЗрдВ рдирд╣реАрдВ рдЬрд╛рдКрдВрдЧрд╛, рдпрд╣ рдорд╛рдирд╛ рдЬрд╛рддрд╛ рд╣реИ рдХрд┐ рдкрд╛рдардХ рдХреЛ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдкрд╛рд▓ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреА рдореВрд▓ рдмрд╛рддреЗрдВ рдкрддрд╛ рд╣реИред
npm install -g sails sails new passport-sails cd passport-sails sails lift 

рдкреАрдПрд╕ рдореИрдВ рд▓рд╛рдЗрд╡-рдХреЛрдбрд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдиреЛрдбрдореЙрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рд╕рд▓рд╛рд╣ рджреЗрддрд╛ рд╣реВрдВред
 npm install -g nodemon 
рддрдм рдкрд╛рд▓ рд▓рд┐рдлреНрдЯ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп рдмрд╕ рдиреЛрдбрдо рдЪрд▓рд╛рдПрдВред

рдкрд╛рд╕рдкреЛрд░реНрдЯ рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ


рдпрд╣рд╛рдВ рд╕рдм рдХреБрдЫ рд╕рд░рд▓ рд╣реИред
 npm install passport npm install passport-local npm install passport-remember-me 

рдПрдХ рдХреБрдХреА рдореЗрдВ рдЯреЛрдХрди рджреНрд╡рд╛рд░рд╛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдпрд╛рдж рд░рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рд▓реЙрдЧрд┐рди, рдкрд╛рд╕рд╡рд░реНрдб рдФрд░ рд░рд┐рдореЗрдореНрдмреЗ рдореА рд░рдгрдиреАрддрд┐ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд▓реЙрдЧ рдЗрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд╛рд╕рдкреЛрд░реНрдЯ, рд╕реНрдерд╛рдиреАрдп рд░рдгрдиреАрддрд┐ рд╕реЗрдЯ рдХрд░реЗрдВред

рдкрд╛рд╕рдкреЛрд░реНрдЯ рд╕реЗрдЯрдЕрдк


рдпрд╣ рд╕рдмрд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рдмрд┐рдВрджреБ рд╣реИред рдкреНрд░рд▓реЗрдЦрди рдЪреМрдЦрдЯреЗ ( рд▓рд┐рдВрдХ ) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдмрд┐рдирд╛ рдорд┐рдбрд▓рд╡реЗрдпрд░ рдХреА рд╕реНрдерд╛рдкрдирд╛ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИред рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдПрдХ рд╣реА рдврд╛рдВрдЪрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдкрд╛рд╕рдкреЛрд░реНрдЯ рд╕реЗрдЯрд┐рдВрдЧ рдХрд╣рд╛рдВ рд▓рд┐рдЦрдиреА рд╣реИ, рдпрд╣ рд╕реНрдкрд╖реНрдЯ рдирд╣реАрдВ рд╣реИред рдЦреИрд░, рдЦреЛрдЬ рдХрд░рддреЗ рд╣реИрдВред

рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реАрдХрд░рдг рдХрд╣рддрд╛ рд╣реИ рдХрд┐ рдЖрдкрдХреЛ рдкрд╛рд╕рдкреЛрд░реНрдЯ.рдпреВрдЬрд╝ () рдФрд░ app.use () рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред
 //   var passport = require('passport'), LocalStrategy = require('passport-local').Strategy; passport.use(new LocalStrategy( function(username, password, done) { User.findOne({ username: username }, function (err, user) { if (err) { return done(err); } if (!user) { return done(null, false, { message: 'Incorrect username.' }); } if (!user.validPassword(password)) { return done(null, false, { message: 'Incorrect password.' }); } return done(null, user); }); } )); app.configure(function() { app.use(express.static('public')); app.use(express.cookieParser()); app.use(express.bodyParser()); app.use(express.session({ secret: 'keyboard cat' })); app.use(passport.initialize()); app.use(passport.session()); app.use(app.router); }); 

рдпрд╣ рдЗрд╕ рдЙрджрд╛рд╣рд░рдг рд╕реЗ рд╕реНрдкрд╖реНрдЯ рд╣реИ рдХрд┐ рд╣рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЪрд░ рддрдХ рдкрд╣реБрдВрдЪ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдЖрдкрдХреЛ рдХрд╛рд░реНрд░рд╡рд╛рдИ рдореЗрдВ рдирд╣реАрдВ, рдмрд▓реНрдХрд┐ рдХрд╣реАрдВ рдФрд░ рдкрд╛рд╕рдкреЛрд░реНрдЯ рд░рдЦрдиреЗ рдХреА рдЬрд░реВрд░рдд рд╣реИред

рдПрдХ рд╕рдВрдХреНрд╖рд┐рдкреНрдд рдЦреЛрдЬ рдХреЗ рдмрд╛рдж, рдПрдХ рд╡рд┐рд╡рд░рдг рд╕рд╛рдордиреЗ рдЖрдпрд╛ рд╣реИ рдЬреЛ Sails.js рдкреНрд░рд▓реЗрдЦрди рдореЗрдВ рдЙрд▓реНрд▓рд┐рдЦрд┐рдд рдирд╣реАрдВ рд╣реИред рдЖрдк Sails рдореЗрдВ рдорд┐рдбрд┐рд▓рд╡реЗрдпрд░ рд▓реЗрдпрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╕рд╛рдордЧреНрд░реА рдХреЗ рд╕рд╛рде config / midware.js рдореЗрдВ рдПрдХ рдлрд╝рд╛рдЗрд▓ рдмрдирд╛рдПрдБ:
 module.exports = { express: { customMiddleware: function(app) {} } }; 

рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдРрдк рддрдХ рдкрд╣реБрдВрдЪ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдкрд╛рд╕рдкреЛрд░реНрдЯ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдХреЗ рд▓рд┐рдП рдиреАрдЪреЗ рдЖ рд╕рдХрддреЗ рд╣реИрдВред
 //config/middleware.js var bcrypt = require('bcrypt'), // bcrypt crypto = require('crypto'), //crypto    token' passport = require('passport'), //passport LocalStrategy = require('passport-local').Strategy, //  RememberMeStrategy = require('passport-remember-me').Strategy; //Remember Me  //   "login sessions" //   serialize\deserialize. passport.serializeUser(function(user, next) { next(null, user.id); }); passport.deserializeUser(function(id, next) { User .findOne(id) .done(function(error, user) { next(error, user); }); }); module.exports = { express: { customMiddleware: function(app) { //   passport.use(new LocalStrategy({ usernameField: 'username', passwordField: 'password' }, function(username, password, next) { //      email' User .findOne() .where({ or: [{ username: username }, { email: username }] }) .done(function(error, user) { // next-callback': //next(error, user, info); if (error) { next(error); } else if (!user) { next(false, false, 'This user not exists'); } else if (!bcrypt.compareSync(password, user.password)) { next(false, false, 'Wrong password'); } else { next(false, user); } }); } )); // RememberMe  passport.use(new RememberMeStrategy({ key: 'token' //  cookie,    token }, function(token, done) { //    token' User .findOne() .where({ autoLoginHash: token }) .done(function(error, user) { if (error) { done(error); } else if (!user) { done(null, false); } else { //  token    delete user.autoLoginHash; user.save(function() {}); done(null, user); } }); }, function(user, done) { //   token var token = crypto.randomBytes(32).toString('hex'); user.autoLoginHash = token; user.save(function() {}); done(null, token); })); app.use(passport.initialize()); app.use(passport.session()); app.use(passport.authenticate('remember-me')); } } }; 

Config / midware.js рдореЗрдВ рд╣рдо рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╕рднреА рд░рдгрдиреАрддрд┐рдпреЛрдВ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рддреЗ рд╣реИрдВред рдЙрд╕рдХреЗ рдмрд╛рдж, рдЖрдк рд╕реАрдзреЗ рдорд╛рд░реНрдЧреЛрдВ рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдЧреЗ рдмрдврд╝ рд╕рдХрддреЗ рд╣реИрдВред

рд╣рдо рдкрд╛рд╕рдкреЛрд░реНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ


рдорд╛рди рд▓реАрдЬрд┐рдП рдХрд┐ рдЖрдкрдХреЗ рдкрд╛рд╕ рдПрдХ рдирд┐рдпрдВрддреНрд░рдХ рд╣реИ рдЬреЛ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЗ рд▓рд┐рдП рдЬрд┐рдореНрдореЗрджрд╛рд░ рд╣реИред рдЗрд╕рдореЗрдВ рд░реВрдЯ рд▓реЙрдЧрд┐рди рдФрд░ рд▓реЙрдЧрдЖрдЙрдЯ рд╣реИ ред
 //api/controllers/AuthController.js module.exports = { login: function(req, res, next) { //  authenticate  LocalStrategy passport.authenticate('local', function(error, user, info) { if (error) { next(new Error('Some error was occured')); } else if (!user) { next(info); } else { //     //    login //     req.login(user, function(error) { if (error) { next(new Error(error)); } else { res.redirect('/my-profile/' + req.user.username); } }); } })(req, res); //IMPORTANT:    ,    authenticate('login', ...)(req,res); //Passport'    \  req.body }, logout: function(req, res, next) { //    token' res.clearCookie('token'); req.logout(); res.redirect('/'); } }; 

рдЖрдк рдЕрдкрдиреА рд╕реНрд╡рдпрдВ рдХреА рддреНрд░реБрдЯрд┐ рд╣реИрдВрдбрд▓рд┐рдВрдЧ рд╡рд┐рдзрд┐рдпреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП req.flash рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ, рдЖрджрд┐ред рд╣рдо рдЗрд╕реЗ рдкрдж рдХреЗ рджрд╛рдпрд░реЗ рд╕реЗ рдмрд╛рд╣рд░ рдЫреЛрдбрд╝ рджреЗрдВрдЧреЗред

Req.user рдСрдмреНрдЬреЗрдХреНрдЯ рдореЗрдВ, рдПрдХ рд╕рдлрд▓ рд▓реЙрдЧрд┐рди рдХреЗ рдмрд╛рдж, рдЖрдкрдХрд╛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдореЙрдбрд▓ рдЭреВрда рд╣реЛрдЧрд╛ред рдЙрд╕реА рддрд░рд╣, рдЖрдк рдЗрд╕реЗ req.user рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЯреЗрдореНрдкреНрд▓реЗрдЯ рдореЗрдВ рдПрдХреНрд╕реЗрд╕ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

рдпрд╣ рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рдХрд╛ рдЕрдВрдд рд╣реИ, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдирд╣реАрдВ ... рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░реЗрдВред рдореБрдЭреЗ рдПрдХ рдФрд░ рддреНрд░реБрдЯрд┐ рдорд┐рд▓реАред рдЗрд╕рд▓рд┐рдП ...

рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ HTTP рдЕрдиреБрд░реЛрдзреЛрдВ рджреНрд╡рд╛рд░рд╛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╕реЙрдХреЗрдЯреНрд╕ рджреНрд╡рд╛рд░рд╛ рдирд╣реАрдВред рдЕрдЪреНрдЫрд╛ рдирд╣реАрдВ рд╣реИ, рдЖрдкрдХреЛ рдирд┐рд░реНрдгрдп рд▓реЗрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред


рдпрджрд┐ рдЖрдк рд╕реЙрдХреЗрдЯреНрд╕ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдкрд╛рд▓реЛрдВ рдореЗрдВ рдиреАрддрд┐рдпрд╛рдВ рдХрд╛рдо рдХрд░рддреА рд╣реИрдВред рддреНрд░реБрдЯрд┐ рдпрд╣ рд╣реИ рдХрд┐ рдиреАрддрд┐ рдореЗрдВ рдореИрдВ рдкрд╛рд╕рдкреЛрд░реНрдЯ req.isAuthenticated () рдкрджреНрдзрддрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реВрдВред рдФрд░ рдмрд╕ рдпрд╣ рдЕрдиреБрд░реЛрдз рдореЗрдВ рдирд╣реАрдВ рд╣реИ рдЬрдм рдЕрдиреБрд░реЛрдз рд╕реЙрдХреЗрдЯ рдкрд░ рдЬрд╛рддрд╛ рд╣реИред Req.login, req.isAuthenticated () рдФрд░ рдЕрдиреНрдп рд▓реЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП рд╕рдорд░реНрдерди рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рдереЛрдбрд╝рд╛ рд╕рд╛ "рдбрд╛рдЙрдирд▓реЛрдб" рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

Config / bootstrap.js рдлрд╝рд╛рдЗрд▓ рдЦреЛрд▓реЗрдВ рдФрд░ рдЗрд╕ рдХреЛрдб рдХреЛ рд▓рд┐рдЦреЗрдВ:
 //config/bootstrap.js module.exports.bootstrap = function(cb) { var passport = require('passport'), // passport http = require('http'), // http initialize = passport.initialize(), session = passport.session(), //  :) methods = ['login', 'logIn', 'logout', 'logOut', 'isAuthenticated', 'isUnauthenticated']; sails.removeAllListeners('router:request'); //  listeners  request' sails.on('router:request', function(req, res) { //   event-listener initialize(req, res, function() { session(req, res, function(error) { if (error) { return sails.config[500](500, req, res); } for (var i = 0; i < methods.length; i++) { //Bind'    req- req[methods[i]] = http.IncomingMessage.prototype[methods[i]].bind(req); } //  sails    route sails.router.route(req, res); }); }); }); //IMPORTANT:    cb() // Sails    cb(); }; 

рдЗрд╕ рдкреНрд░рдХрд╛рд░, рдкреНрд░рддреНрдпреЗрдХ рдЕрдиреБрд░реЛрдз рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рд╕реЗ рдкрд╣рд▓реЗ, рд╕рднреА рд▓рд╛рдкрддрд╛ рдкрд╛рд╕рдкреЛрд░реНрдЯ рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЛ рд░реА-рдСрдмреНрдЬреЗрдХреНрдЯ рдореЗрдВ рдЗрдВрдЬреЗрдХреНрдЯ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

рдореБрдЭреЗ рдЙрдореНрдореАрдж рд╣реИ рдХрд┐ рдпрд╣ рдкреЛрд╕реНрдЯ Node.js + Sails рдкрд░ рдиреМрд╕рд┐рдЦрд┐рдпрд╛ рдбреЗрд╡рд▓рдкрд░реНрд╕ рдХреЛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдХреА рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдХрд░рддреА рд╣реИред рдЕрдВрдд рддрдХ рдкрдврд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж :)

рдкрд╛рд▓
рдкрд╛рд╕рдкреЛрд░реНрдЯ
рдкрд╛рд╕рдкреЛрд░реНрдЯ рд╕реНрдерд╛рдиреАрдп рд░рдгрдиреАрддрд┐
рдкрд╛рд╕рдкреЛрд░реНрдЯ рдореА рдореА рд╕реНрдЯреНрд░реЗрдЯрдЬреА
рд╕реЛрд╢рд▓ рдиреЗрдЯрд╡рд░реНрдХрд┐рдВрдЧ рдПрдХреАрдХрд░рдг рдЙрджрд╛рд╣рд░рдг - sails-social-schem-example

Source: https://habr.com/ru/post/In211925/


All Articles