#TernopilJS
Лекція 11. Going to production
Лектор - Михайло
Код - github.com/ternopiljs
Презентації - slides.com/ternopiljs
Новини - vk.com/ternopiljs
Безпека
Рефакторинг
Продуктивність
Приховання секретних ключів
S3.config = {
key: '12345678901234567890',
secret: '098765432109876543210987654321',
bucket: 'top-secret-bucket'
};
S3.config = {
key: Meteor.settings.aws.key,
secret: Meteor.settings.aws.secret,
bucket: Meteor.settings.aws.bucket
};
Безпека
Приховання секретних ключів
{
"public": {
"aws": {
"bucketUrl": "https://my-meteor-example.s3.amazonaws.com",
}
},
"aws": {
"accessKey": "12345678901234567890",
"secret": "098765432109876543210987654321",
"bucket": "top-secret-bucket"
}
}
config/production/settings.json
Безпека
Приховання секретних ключів
$ meteor run --settings config/development/settings.json
$ METEOR_SETTINGS={"public": {"key": "123"}} meteor run
> Meteor.settings.aws
undefined
> Meteor.settings.aws
{
accessKey: "12345678901234567890",
secret: "098765432109876543210987654321",
bucket: "top-secret-bucket"
}
client
server
Клієнт має доступ тільки до властивості public!
> Meteor.settings
{
public: {
key: "123"
}
}
Безпека
Приховання серверного коду
if (Meteor.isClient) {
// do some stuff
}
if (Meteor.isServer) {
ServiceConfiguration.configurations.insert({
service: "instagram",
clientId: Meteor.settings.instagram.clientId,
secret: Meteor.settings.instagram.secret,
scope: 'basic+likes+comments'
});
}
both/accounts.js
Безпека
Приховання серверного коду
ServiceConfiguration.configurations.insert({
service: "instagram",
clientId: Meteor.settings.instagram.clientId,
secret: Meteor.settings.instagram.secret,
scope: 'basic+likes+comments'
});
server/accounts.js
// do some stuff
client/accounts.js
Безпека
Обмеження доступу до БД
$ meteor remove autopublish & meteor remove insecure
Безпека
Приховання полів при публікації
Meteor.publish('users', function(limit) {
return Meteor.users.find({}, {limit: limit});
);
Meteor.publish('users', function(limit) {
var projection = {
'username': 1,
'createdAt': 1,
'avatar': 1
};
return Meteor.users.find({}, {fields: projection, limit: limit});
);
Безпека
Приховання полів при публікації
Meteor.publish(null, function(limit) {
if (this.userId) {
var projection = {
'settings': 1,
'roles': 1
};
return Meteor.users.find({}, {fields: projection, limit: limit});
} else {
this.ready();
}
});
Розширення документа поточного користувача
Meteor.publish(null, function() {
// some stuff
});
Публікація, ім'я якої встановлене як null, автоматично відправляє дані до всіх під'єднаних клієнтів.
Безпека
Валідація даних
Meteor.methods({
'messages/new': function (message) {
message.createdAt = new Date;
return Messages.insert(message);
}
});
Template.Messages.events({
'submit form': function (e) {
var message;
// ... формуєм повідомлення
Meteor.call('messages/new', message);
}
});
server
client
Безпека
Валідація даних
// server
Meteor.methods({
'messages/new': function (message) {
message.createdAt = new Date;
return Messages.insert(message);
}
});
// client
Template.Messages.events({
'submit form': function (e) {
var message;
// ... формуєм повідомлення
check(message, {
userId: this.userId,
conversationId: String,
addresseeId : Match.Optional(String),
text: Match.Optional(String),
attachments: Match.Optional({
images: Match.Optional([String]),
docs: Match.Optional([String])
}),
});
Meteor.call('messages/new', message);
}
});
server
client
Безпека
Валідація даних на сервері
//server
Meteor.methods({
'messages/new': function (message) {
check(message, {
userId: this.userId,
conversationId: String,
addresseeId : Match.Optional(String),
text: Match.Optional(String),
attachments: Match.Optional({
images: Match.Optional([String]),
docs: Match.Optional([String])
}),
});
message.createdAt = new Date;
return Messages.insert(message);
}
});
// client
Template.Messages.events({
'submit form': function (e) {
var message;
// ... формуєм повідомлення
Meteor.call('messages/new', message);
}
});
server
client
Безпека
User profile services
Meteor.users.deny({
update: function () {
return true;
}
});
Meteor.services - editable by default
Sollution:
Безпека
Захист від DDoS
$ meteor add meteorhacks:sikka
Безпека
Content Security Policy/X-Frame-Options
$ meteor add browser-policy
Meteor.startup(function() {
BrowserPolicy.content.allowImageOrigin("https://*.googleusercontent.com");
BrowserPolicy.content.allowImageOrigin("http://profile.ak.fbcdn.net");
BrowserPolicy.content.allowImageOrigin("http://graph.facebook.com");
BrowserPolicy.content.disallowInlineScripts(); // забороняє виконання inline javascript
});
server
Events
Template.Comment.events({
'click .like': function (e) {
var text = $(e.target).closest('.comment-wrapper')
.find('.comment-text')
.text();
}
});
Template.Comment.events({
'click .like': function (e) {
var instance = Template.instance();
var text = instance.$('.comment-text').text();
}
});
Рефакторинг
Scoped reactivity
// тільки в крайній необхідності
var limit = new ReactiveVar;
Template.Comment.helpers({
limit: function () {
return limit.get();
}
});
Template.Comment.helpers({
limit: function () {
return Template.instance().limit.get();
}
});
Template.Comment.onCreated(function () {
this.limit = new ReactiveVar;
});
Рефакторинг
Scoped autorun/subscribe
Template.Comment.onRendered(function () {
Tracker.autorun(function () { /*...*/ });
Meteor.subscribe(/*...*/);
});
Template.Comment.onRendered(function () {
this.autorun(function () { /*...*/ });
this.subscribe(/*...*/);
});
Рефакторинг
Templates
<template name="Comments">
{{#each comments}}
<div>{{text}}</div>
{{#with likes}}
<div>{{count}}</div>
{{/with}}
{{/each}}
</template>
<template name="Comments">
{{#each comments}}
{{> Comment}}
{{/each}}
</template>
Рефакторинг
Templates
<template name="Comments">
{{#each comments}}
<div>{{text}}</div>
{{#with likes}}
<div>{{count}}</div>
{{/with}}
{{/each}}
</template>
<template name="Comments">
{{#each comments}}
{{> Comment}}
{{/each}}
</template>
Рефакторинг
Meteor style guide
Рефакторинг
JSHint
$ npm install jshint -g
$ jshint .
Відключення реактивності
Comments.find({ postId: this.postId }, { reactive: false });
Продуктивність
Kadira
Продуктивність
this.unblock()
Meteor.methods({
getLocation: function() {
Meteor.inServer && Meteor._sleepForMs(2000);
}
});
Meteor.methods({
getLocation: function() {
this.unblock();
Meteor.inServer && Meteor._sleepForMs(2000);
}
});
if (Meteor.isClient) {
Meteor.call('trackLocation');
Meteor.subscribe('posts');
}
Продуктивність
Send less data
Meteor.publish('getRecentPosts', function () {
return Posts.find({}, {limit: 50});
});
Meteor.publish('getRecentPosts', function () {
var options = {
sort: {date: -1},
limit: 50,
fields: {topic:1, content: 1}
};
return Posts.find({}, options);
});
Продуктивність
Cached subscriptions
$ meteor add meteorhacks:subs-manager
var subscriptions = new SubsManager();
Router.config({
waitOn: function () {
return Meteor.subscribe('notifications');
}
});
Router.route('/', {
name: 'home',
waitOn: function () {
return subscriptions.subscribe('posts');
}
});
У наступній лекції:
Д/З