chore(i18n,curriculum): processed translations - new ukrainian (#44447)

This commit is contained in:
camperbot
2021-12-10 11:14:24 +05:30
committed by GitHub
parent 8651ee1797
commit 0473dedf47
1663 changed files with 156692 additions and 1 deletions

View File

@@ -0,0 +1,90 @@
---
id: 589fc832f9fc0f352b528e78
title: Повідомлення про нових користувачів
challengeType: 2
forumTopicId: 301546
dashedName: announce-new-users
---
# --description--
Багато чатів мають функцію повідомлення всіх підключених користувачів чату про приєднання чи від'єднання користувача. Оскільки приєднання чи від'єднання це вже виконання дії, то для підтримки такої функції вам потрібно відредагувати цю подію. Найбільш логічний спосіб зробити це — надіслати певні дані з подією: ім’я користувача, який приєднався/від'єднався, поточну кількість користувачів, а також те, чи це ім’я приєднане чи від'єднане.
Змініть назву події на `'user'` і надішліть інформацію, що містить поля 'name', 'currentUsers' і 'connected' (`true` у разі приєднання, або - `false` для відключення користувача). Обов’язково переконайтесь, що ви змінили події 'user count' та встановили `false` при відключенні для поля 'connected' замість `true`, як і при підключенні.
```js
io.emit('user', {
name: socket.request.user.name,
currentUsers,
connected: true
});
```
Тепер ваш клієнт матиме всю необхідну інформацію, щоб правильно показати кількість поточних користувачів і повідомити, коли користувач підключається або відключається! Щоб обробити цю подію зі сторони клієнта, ми повинні отримати `'user'`, а потім оновити кількість поточних користувачів за допомогою jQuery, щоб замінити текст `#num-users` на `'{NUMBER} users online'`, а також додати `<li>` до невпорядкованого маркованого списку з id `messages` з `'{NAME} has {joined/left} the chat.'`.
Ця дія може виглядати таким чином:
```js
socket.on('user', data => {
$('#num-users').text(data.currentUsers + ' users online');
let message =
data.name +
(data.connected ? ' has joined the chat.' : ' has left the chat.');
$('#messages').append($('<li>').html('<b>' + message + '</b>'));
});
```
Відправте сторінку, якщо все було виконано правильно. Якщо виникла помилка, ви можете перевірити виконання проєкту до цього етапу [тут](https://gist.github.com/camperbot/bf95a0f74b756cf0771cd62c087b8286).
# --hints--
Подія `'user'` повинна містити name, currentUsers та connected.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/io.emit.*('|")user\1.*name.*currentUsers.*connected/gis,
'You should have an event emitted named user sending name, currentUsers, and connected'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Клієнт повинен правильно обробляти та показувати нові дані із події `'user'`.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/public/client.js').then(
(data) => {
assert.match(
data,
/socket.on.*('|")user\1[^]*num-users/gi,
'You should change the text of "#num-users" within on your client within the "user" event listener to show the current users connected'
);
assert.match(
data,
/socket.on.*('|")user\1[^]*messages.*li/gi,
'You should append a list item to "#messages" on your client within the "user" event listener to announce a user came or went'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,96 @@
---
id: 5895f70df9fc0f352b528e68
title: Стратегії автентифікації
challengeType: 2
forumTopicId: 301547
dashedName: authentication-strategies
---
# --description--
Стратегія - це процес авторизації користувача. Ви можете вибрати стратегію, яка дозволяє користувачам авторизуватись на основі збережених даних (якщо ви їх вказували) або від різних провайдерів таких, як Google або GitHub. До цього проєкту ми встановимо локальну стратегію. Для того, щоб переглянути перелік сотень стратегій, перейдіть на сайт Passport [сюди](http://passportjs.org/).
Вкажіть `passport-local@~1.0.0` як залежний елемент і додайте його на ваш сервер, як показано далі: `const LocalStrategy = require('passport-local');`
Далі ви повинні вказати паспорту (passport) **використати (use)** макет об'єкту LocalStrategy із певними вказаними параметрами. Переконайтесь у тому, що це (як і все з цього моменту) інкапсульовано в з'єднанні з базою даних (database), оскільки воно залежить від цього!
```js
passport.use(new LocalStrategy(
function(username, password, done) {
myDataBase.findOne({ username: username }, function (err, user) {
console.log('User '+ username +' attempted to log in.');
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (password !== user.password) { return done(null, false); }
return done(null, user);
});
}
));
```
Так виглядає процес, коли ми хочемо авторизувати когось локально. Спершу система шукає користувача у нашій базі даних за допомогою введеного ім'я користувача, потім перевіряє його за паролем, і тоді, якщо не виникло жодних помилок під час перевірки, як, наприклад, невірний пароль, об'єкт `user` повертається та вважається авторизованим.
Багато стратегій створюються на основі різних налаштувань, але, як правило, це легко зробити звернувшись до файлу README у репозиторії цієї стратегії. Як хороший приклад можна зазначити стратегію GitHub, де ми можемо не переживати через ім'я користувача або пароль, оскільки користувач автоматично переадресовується на сторінку аутентифікації GitHub для авторизації. Допоки користувач буде залишатись авторизованим і буде згідний з умовами, GitHub надаватиме його профіль для користування.
Далі ми дізнаємося, як за допомогою стратегії авторизації перевірити користувача на основі заповнених даних!
Відправте вашу сторінку, якщо все зрозуміло. Якщо сталась якась помилка, ви маєте змогу перевірити статус проєкту до цього етапу [ тут ](https://gist.github.com/camperbot/53b495c02b92adeee0aa1bd3f3be8a4b).
# --hints--
Passport-local повинен бути залежністю.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'passport-local',
'Your project should list "passport-local " as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Запит Passport-local повинен бути вірно надісланий та налаштований.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/require.*("|')passport-local("|')/gi,
'You should have required passport-local'
);
assert.match(
data,
/new LocalStrategy/gi,
'You should have told passport to use a new strategy'
);
assert.match(
data,
/findOne/gi,
'Your new local strategy should use the findOne query to find a username based on the inputs'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,154 @@
---
id: 589fc831f9fc0f352b528e77
title: Автентифікація за допомогою Socket.IO
challengeType: 2
forumTopicId: 301548
dashedName: authentication-with-socket-io
---
# --description--
Наразі ви не можете визначити, хто підключений до вашого веб-сокету. Хоча й `req.user` містить об'єкт "користувач", проте тільки тоді, коли ваш користувач взаємодіє з веб-сервером, тоді ж як до веб-сокетів у вас немає `req` (запиту), а, як наслідок, немає і даних користувача. Одним зі способів розв'язання проблеми відсутності інформації про те, хто підключений до вашого веб-сокету, є застосування синтаксичного аналізу та декодування файлів cookie, які містять сесію паспорту, далі - отриманні дані десеріалізуються задля отримання об'єкту "користувач". На щастя, NPM має спеціальний пакет для цього, який перетворює колись складну задачу на щось просте!
Додайте `passport.socketio@~3.7.0`, `connect-mongo@~3.2.0` та `cookie-parser@~1.4.5` як залежності та встановіть(require) їх як `passportSocketIo`, `MongoStore` та `cookieParser` відповідно. Крім того, нам потрібно ініціалізувати нове сховище пам'яті з `express-session`, яке ми вже потребували (required). Воно повинно мати такий вигляд:
```js
const MongoStore = require('connect-mongo')(session);
const URI = process.env.MONGO_URI;
const store = new MongoStore({ url: URI });
```
Тепер треба лише вказати Socket.IO використовувати його та встановити налаштування. Переконайтеся, що його додано перед наявним кодом сокета, а не в цьому слухачі підключення. На вашому сервері це повинно мати такий вигляд:
```js
io.use(
passportSocketIo.authorize({
cookieParser: cookieParser,
key: 'express.sid',
secret: process.env.SESSION_SECRET,
store: store,
success: onAuthorizeSuccess,
fail: onAuthorizeFail
})
);
```
Зауважте, що налаштування автентифікації Passport для Socket.IO є дуже подібним до налаштування проміжного програмного забезпечення `session` для API. Це пояснюється тим, що вони призначені для одного методу автентифікації: отримати id сесії від файлів cookie та перевірити його.
Раніше, при налаштуванні проміжного програмного забезпечення `session`, ми не встановлювали безпосередньо назву файлів cookie для сесії (`key`). Це пояснюється тим, що пакет `session` використовував значення за замовчуванням. Тепер, коли додано інший пакет, який потребує доступу до того самого значення файлів cookies, треба чітко встановити значення `key` в обох об'єктах конфігурації.
Обов'язково виконайте додавання `key` з назвою файлів cookie до проміжного програмного забезпечення `session`, яке збігається з ключем Socket.IO. Також додайте посилання `store` на налаштування, поруч із якими ми пропишемо `saveUninitialized: true`. Необхідно вказати Socket.IO до якої сесії прив'язуватися.
<hr />
Тепер слід визначити функції зворотного зв'язку `success` та `fail`:
```js
function onAuthorizeSuccess(data, accept) {
console.log('successful connection to socket.io');
accept(null, true);
}
function onAuthorizeFail(data, message, error, accept) {
if (error) throw new Error(message);
console.log('failed connection to socket.io:', message);
accept(null, false);
}
```
Об'єкт "користувач" тепер має доступ у вашому сокет-об'єкті як `socket.request.user`. Тепер, наприклад, ви можете додати таке:
```js
console.log('user ' + socket.request.user.name + ' connected');
```
Це дозволить увійти у підключену консоль серверу!
Відправте сторінку, якщо все було виконано правильно. Якщо сталась якась помилка, то можна перевірити проєкт до цього етапу [ тут ](https://gist.github.com/camperbot/1414cc9433044e306dd7fd0caa1c6254).
# --hints--
`passport.socketio` має бути залежністю.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'passport.socketio',
'Your project should list "passport.socketio" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
`cookie-parser` має бути залежністю.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'cookie-parser',
'Your project should list "cookie-parser" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
passportSocketIo має правильно виконуватися (required).
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/require\((['"])passport\.socketio\1\)/gi,
'You should correctly require and instantiate "passport.socketio"'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
passportSocketIo слід належним чином налаштувати.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/io\.use\(\s*\w+\.authorize\(/,
'You should register "passport.socketio" as socket.io middleware and provide it correct options'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,64 @@
---
id: 589690e6f9fc0f352b528e6e
title: Очистити ваш проект з модулями
challengeType: 2
forumTopicId: 301549
dashedName: clean-up-your-project-with-modules
---
# --description--
Зараз все, що ви маєте є у файлі `server.js`. Це може привести до складного управління кодом, який не дуже розширюється. Створіть 2 нових файли: `routes.js` і `auth.js`
Обидва повинні починатися з такого коду:
```js
module.exports = function (app, myDataBase) {
}
```
Тепер, зверху вашого серверного файлу, подайте ці файли так: `const routes = require('./routes.js');`. Одразу після того, як ви встановите вдалий зв'язок з базою даних, пропишіть кожен з них таким чином: `routes(app, myDataBase)`
Врешті, візьміть усі ваші маршрути на сервері та вставте їх в нові файли, і видаліть їх з вашого серверного файлу. Також візьміть функцію `ensureAuthenticated`, оскільки її створили спеціально для маршрутизації. Тепер ви маєте правильно додати залежності, в яких використовуються функції, такі як `const passport = require('passport');`, з самого верху, над експортним рядком у вашому файлі `routes.js`.
Продовжуйте додавати їх, поки помилки не припинять існувати, а ваш серверний файл більше не матиме маршрутизування (**за винятком шляху в блок catch**)!
Тепер зробіть те саме в вашому файлі auth.js з усім, що пов'язане з автентифікацією, як ось серіалізація і налаштування локальної стратегії, і видаліть їх з вашого серверного файлу. Обов'язково додайте залежності і наберіть `auth(app, myDataBase)` на сервері в цьому ж місці.
Підтвердьте вашу сторінку, якщо все зрозуміло. Якщо ви натрапите на помилки, можете перевірити виконання проєкту до цього етапу [тут](https://gist.github.com/camperbot/2d06ac5c7d850d8cf073d2c2c794cc92).
# --hints--
Модулі мають бути присутні.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/require\s*\(('|")\.\/routes(\.js)?\1\)/gi,
'You should have required your new files'
);
assert.match(
data,
/client\s*\.db[^]*routes/gi,
'Your new modules should be called after your connection to the database'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,107 @@
---
id: 589fc831f9fc0f352b528e75
title: Спілкування за допомогою Emitting
challengeType: 2
forumTopicId: 301550
dashedName: communicate-by-emitting
---
# --description--
<dfn>Emit</dfn> — найпоширеніший спосіб спілкування, яким ви будете користуватись. Коли ви висвічуєте (emit) щось зі сервера до 'io', ви надсилаєте назву події й дані всім під'єднаним сокетам. Хорошим прикладом цієї концепції було б висвітлення (emitting) поточного рахунку під'єднаних користувачів щоразу коли під'єднується новий користувач!
Почніть із додавання змінної, щоб відстежувати користувачів, безпосередньо перед тим місцем, де ви зараз слухаєте зв'язки.
```js
let currentUsers = 0;
```
Тепер, коли хтось приєднується, ви повинні збільшити рахунок перш ніж висвітити його. Отже, ви додаєте інкремент в межах слухача підключення.
```js
++currentUsers;
```
Врешті, після збільшення рахунку, ви повинні висвітити подію (все ще в слухачі підключення). Ця подія повинна мати назву "user count", а дані — просто `currentUsers`.
```js
io.emit('user count', currentUsers);
```
Тепер ви можете застосувати цей спосіб для вашого клієнта, аби він міг прослухати цю подію! Подібно до прослуховування з'єднання на сервері, ви будете використовувати ключове слово `on`.
```js
socket.on('user count', function(data) {
console.log(data);
});
```
Тепер спробуйте завантажити ваш додаток, автентифікуватись і ви повинні побачити на консолі клієнта '1', яка показує поточну кількість користувачів! Спробуйте завантажити більше клієнтів і автентифікуйтеся, щоб побачити, як число зростає.
Відправте сторінку, якщо все було виконано правильно. Якщо ви зіткнулись з помилкою, ви можете перевірити зроблений проєкт до цього етапу [here](https://gist.github.com/camperbot/28ef7f1078f56eb48c7b1aeea35ba1f5).
# --hints--
currentUsers мають бути визначеними.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/currentUsers/gi,
'You should have variable currentUsers defined'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Сервер має висвічувати поточний рахунок користувачів в кожному новому зв'язку.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/io.emit.*('|")user count('|").*currentUsers/gi,
'You should emit "user count" with data currentUsers'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Ваш клієнт повинен прослухати подію 'user count'.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/public/client.js').then(
(data) => {
assert.match(
data,
/socket.on.*('|")user count('|")/gi,
'Your client should be connection to server with the connection defined as socket'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,87 @@
---
id: 5895f70df9fc0f352b528e6a
title: Створення нового підпрограмного забезпечення
challengeType: 2
forumTopicId: 301551
dashedName: create-new-middleware
---
# --description--
На практиці будь-який користувач може просто перейти до `/profile`, незалежно від того, пройшов він автентифікацію чи ні, ввівши Url-адресу. Ми хочемо запобігти цьому, перевіривши, чи користувач автентифікувався, перш ніж відображати сторінку профілю. Це прекрасний приклад того, коли можна створити підпрограмне забезпечення.
Це завдання завдає функцію підпрограмного забезпечення `ensureAuthenticated(req, res, next)`, яка буде перевіряти чи користувач автентифікувався методом `isAuthenticated` по `request`, який у свою чергу визначає `req.user`. Якщо це так, то слід викликати `next()`, інакше ми можемо просто відповісти на запит перенаправленням на нашу головну сторінку для входу. Впровадження цього підпрограмного забезпечення означає:
```js
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/');
};
```
Тепер додайте *ensureAuthenticated* як підпрограмне забезпечення до запиту сторінки профілю перед аргументом, щоб отримати запит, який містить функцію для відображення сторінки.
```js
app
.route('/profile')
.get(ensureAuthenticated, (req,res) => {
res.render(process.cwd() + '/views/pug/profile');
});
```
Підтвердьте сторінку, якщо все виконано вірно. Якщо стались помилки, ви можете перевірити виконання проєкту до цього етапу [тут](https://gist.github.com/camperbot/ae49b8778cab87e93284a91343da0959).
# --hints--
Підпрограмне забезпечення sureAuthenticated має бути реалізованим і на нашому /profile маршруті.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/ensureAuthenticated[^]*req.isAuthenticated/gi,
'Your ensureAuthenticated middleware should be defined and utilize the req.isAuthenticated function'
);
assert.match(
data,
/profile[^]*get[^]*ensureAuthenticated/gi,
'Your ensureAuthenticated middleware should be attached to the /profile route'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Запит на отримання /profile має правильно переспрямовувати до / оскільки ми не автентифіковані.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/profile').then(
(data) => {
assert.match(
data,
/Home page/gi,
'An attempt to go to the profile at this point should redirect to the homepage since we are not logged in'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,69 @@
---
id: 589fc831f9fc0f352b528e76
title: Оброблення відключення
challengeType: 2
forumTopicId: 301552
dashedName: handle-a-disconnect
---
# --description--
Ви можете звернути увагу на те, що до цього часу Ви мали тільки збільшення кількості користувачів. Обробка відключення користувача так само легка, як і робота з початковим підключенням, окрім випадків, коли ви повинні слухати його на кожному сокеті, а не на всьому сервері.
Щоб зробити це, додайте іншого слухача всередині існуючого `'connect'` слухача, який слухає `'disconnect'` на сокеті без обробки даних. Ви можете протестувати ці функції, просто увійшовши до консолі, від якої користувач відключився.
```js
socket.on('disconnect', () => {
/*anything you want to do on disconnect*/
});
```
Щоб переконатися, що клієнти постійно мають оновлену кількість користувачів, ви повинні зменшити поточних користувачів (currentUsers) на 1, поки відбувається від'єднання потім викликати 'user count' з оновленою кількістю!
**Примітка:** Як і `'disconnect'`, всі інші події, які сокет може передавати на сервер слід обробити в процесі підключення слухача де ми маємо 'сокет'.
Підтвердіть вашу сторінку, якщо все зрозуміло. Якщо сталась якась помилка, ви маєте змогу перевірити зроблений проєкт до цього етапу [тут](https://gist.github.com/camperbot/ab1007b76069884fb45b215d3c4496fa).
# --hints--
Сервер повинен обробляти подію відключення від сокету.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(data, /socket.on.*('|")disconnect('|")/gi, '');
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Ваш клієнт повинен слідкувати за 'user count' кодом.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/public/client.js').then(
(data) => {
assert.match(
data,
/socket.on.*('|")user count('|")/gi,
'Your client should be connection to server with the connection defined as socket'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,86 @@
---
id: 58a25c98f9fc0f352b528e7f
title: Хешування ваших паролів
challengeType: 2
forumTopicId: 301553
dashedName: hashing-your-passwords
---
# --description--
Якщо ви повернетесь до розділу з інформаційної безпеки, пригадаєте, що зберігання звичайних паролів *не* є гарною ідеєю. Тепер час реалізувати BCrypt для вирішення даної проблеми.
Додайте `bcrypt@~5.0.0` як залежність та викличте її у своєму сервері. Вам буде потрібно виконати гешування у двох ключових місцях: де ви виконуєте реєстрацію/збереження нового облікового запису і коли ви переконуєтесь, що пароль правильний при вході в систему.
Наразі під час реєстрації ви вводите пароль користувача у базу даних таким чином: `password: req.body.password`. Легкий спосіб зберегти хеш це додати перед логікою бази даних: `const hash = bcrypt.hashSync(req.body.password, 12);` та замінити в базі даних `req.body.password`, зберігши лише `password: hash`.
Зрештою, для нашого способу автентифікації перевіримо наступне в нашому коді перед завершенням процесу: `if (password !== user.password) { return done(null, false); }`. Після внесення попередніх змін тепер `user.password` є хешем. Перш ніж вносити зміни до наявного коду, зверніть увагу на те, як команда перевіряє, чи пароль **не** збігається та потім повертає відповідь — не автентифіковано. Майте це на увазі, ваш код може виглядати наступним чином для правильної перевірки пароля, введеного з хешу:
```js
if (!bcrypt.compareSync(password, user.password)) {
return done(null, false);
}
```
Це все, що потрібно, щоб реалізувати одну з найважливіших функцій безпеки, коли вам потрібно зберігати паролі!
Підтвердьте вашу сторінку, якщо все зрозуміло. Якщо виникла помилка, ви можете перевірити виконання проєкту до цього етапу [тут](https://gist.github.com/camperbot/dc16cca09daea4d4151a9c36a1fab564).
# --hints--
BCrypt повинен бути залежністю.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'bcrypt',
'Your project should list "bcrypt" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
BCrypt повинен бути правильно заданий та реалізований.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/require.*("|')bcrypt\1/gi,
'You should have required bcrypt'
);
assert.match(
data,
/bcrypt.hashSync/gi,
'You should use hash the password in the registration'
);
assert.match(
data,
/bcrypt.compareSync/gi,
'You should compare the password to the hash in your strategy'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,57 @@
---
id: 5895f70ef9fc0f352b528e6b
title: Як створити профіль
challengeType: 2
forumTopicId: 301554
dashedName: how-to-put-a-profile-together
---
# --description--
Після того, як ми запевнили, що користувач, який має доступ до `/profile`, автентифікований, ми зможемо використовувати інформацію з `req.user` на нашій сторінці!
Передайте об'єкт, що має властивість `username` і значення `req.user.username` як другий аргумент для методу відображення профілю. Тепер перейдіть до перегляду свого `profile.pug` і додайте наступний рядок під уже наявним `h1` елементом, дотримуючись тих самих відступів:
```pug
h2.center#welcome Welcome, #{username}!
```
Це створить елемент `h2` з класом '`center`' і id '`welcome`', що міститиме текст '`Welcome,`' та ім'я користувача.
Також в `profile.pug` додайте посилання на маршрут `/logout`, який міститиме метод для скасування автентифікації користувача.
```pug
a(href='/logout') Logout
```
Відправте сторінку, якщо все було виконано правильно. Якщо ви натрапили на помилки, ви можете перевірити виконання проєкту до цього етапу [here](https://gist.github.com/camperbot/136b3ad611cc80b41cab6f74bb460f6a).
# --hints--
Ви повинні правильно додати змінну Pug до /profile.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/username:( |)req.user.username/gi,
'You should be passing the variable username with req.user.username into the render function of the profile page'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,79 @@
---
id: 5895f70df9fc0f352b528e69
title: Як використовувати Passport стратегії
challengeType: 2
forumTopicId: 301555
dashedName: how-to-use-passport-strategies
---
# --description--
У заданому файлі `index.pug` є форма авторизації. Її раніше було приховано через вбудований JavaScript з формою `if showLogin` та відступом після неї. Перед `showLogin` у вигляді змінної яка ніколи не була визначена, тому вона ніколи не передавала блок коду, що містить форму. Перейдіть вперед і на `res.render` для цієї сторінки додайте нову змінну до об'єкта `showLogin: true`. Коли ви оновите сторінку, ви повинні побачити форму! Ця форма налаштована для надсилання **POST** запиту до `/login`, тому тут ми повинні прийняти POST і авторизувати користувача.
Для цього завдання необхідно додати маршрут `/login`, щоб прийняти запит POST. Для автентифікації на цьому маршруті, вам потрібно додати підпрограмне забезпечення, щоб зробити це перед тим як надсилати відповідь. Це можна зробити простим передаванням іншого аргументу з підпрограмного забезпечення перед `function(req,res)` з вашою відповіддю! Треба використати таке підпрограмне забезпечення: `passport.authenticate('local')`.
`passport.authenticate` може також приймати деякі опції як аргумент: `{ failureRedirect: '/' }`, який є неймовірно корисними, тому будьте впевнені в тому, що також додали і його. Відповідь після використання підпрограмного забезпечення (яка буде викликана якщо автентифікація підпрограмного забезпечення виконана успішно) повинна переадресувати користувача до `/profile` і цей маршрут повинен зобразити вид `profile.pug`.
Якщо автентифікація була успішною, то об'єкт користувач буде збережено в `req.user`.
Зараз, якщо ви введете ім'я користувача і пароль у форму, вас має переадресувати на домашню сторінку `/`, а консоль вашого серверу повинна показувати `'User {USERNAME} attempted to log in.'`, оскільки ми на цей час не можемо увійти в обліковий запис користувача, який не зареєстрований.
Підтвердіть сторінку, якщо все зрозуміло. Якщо сталась якась помилка, ви маєте змогу перевірити статус проєкту до цього етапу [here](https://gist.github.com/camperbot/7ad011ac54612ad53188b500c5e99cb9).
# --hints--
Усі кроки повинні бути правильно реалізовані в server.js.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/showLogin:( |)true/gi,
'You should be passing the variable "showLogin" as true to your render function for the homepage'
);
assert.match(
data,
/failureRedirect:( |)('|")\/('|")/gi,
'Your code should include a failureRedirect to the "/" route'
);
assert.match(
data,
/login[^]*post[^]*local/gi,
'You should have a route for login which accepts a POST and passport.authenticates local'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Запит POST для /login повинен правильно переадресовувати до /.
```js
(getUserInput) =>
$.post(getUserInput('url') + '/login').then(
(data) => {
assert.match(
data,
/Looks like this page is being rendered from Pug into HTML!/gi,
'A login attempt at this point should redirect to the homepage since we do not have any registered users'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,91 @@
---
id: 5895f70cf9fc0f352b528e67
title: Впровадження Серіалізації Користувача у Паспорт
challengeType: 2
forumTopicId: 301556
dashedName: implement-the-serialization-of-a-passport-user
---
# --description--
Зараз ми не завантажуємо поточний об’єкт користувача, оскільки ми ще не налаштували нашу базу даних. Це можна зробити багатьма різними способами, але для нашого проєкту ми під'єднаємось до бази даних один раз при запуску сервера і збережемо постійне з'єднання протягом усього життєвого циклу додатку. Для цього додайте рядок з'єднання вашої бази даних (наприклад: `mongodb+srv://:@cluster0-jvwxi.mongodb.net/?retryWrites=true&w=majority`) до змінної середовища `MONGO_URI`. Це використовується у файлі `connection.js`.
*Ви можете створити безкоштовну базу даних на [MongoDB Atlas](https://www.mongodb.com/cloud/atlas).*
Тепер ми хочемо підключитися до нашої бази даних, а після того почати створювати запити. Це робимо з метою, щоб заборонити запити до того, як наша база даних буде підключена або якщо у неї виникне помилка. Для досягнення цього вам потрібно включити свою серіалізацію та маршрути додатків у такий код:
```js
myDB(async client => {
const myDataBase = await client.db('database').collection('users');
// Be sure to change the title
app.route('/').get((req, res) => {
//Change the response to render the Pug template
res.render('pug', {
title: 'Connected to Database',
message: 'Please login'
});
});
// Serialization and deserialization here...
// Be sure to add this...
}).catch(e => {
app.route('/').get((req, res) => {
res.render('pug', { title: e, message: 'Unable to login' });
});
});
// app.listen out here...
```
Не забудьте розкоментувати код `myDataBase` в `deserializeUser` та відредагувати свій `done(null, null)` для того, щоб включити `doc`.
Відправте сторінку, якщо все було виконано правильно. Якщо ви зіткнулись з помилкою, ви можете перевірити зроблений проєкт до цього етапу [тут](https://gist.github.com/camperbot/175f2f585a2d8034044c7e8857d5add7).
# --hints--
Повинно бути присутнім з'єднання з базою даних.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/').then(
(data) => {
assert.match(
data,
/Connected to Database/gi,
'You successfully connected to the database!'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Тепер десеріалізація повинна виконуватися правильно у БД, а `done(null, null)` повинна викликатися із `doc`.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/null,\s*doc/gi,
'The callback in deserializeUser of (null, null) should be altered to (null, doc)'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,114 @@
---
id: 589a69f5f9fc0f352b528e71
title: Реалізація соціальної автентифікації ІІ
challengeType: 2
forumTopicId: 301557
dashedName: implementation-of-social-authentication-ii
---
# --description--
Остання частина налаштування автентифікації GitHub це створення самої стратегії. Для цього вам потрібно буде додати залежність від `passport-github@~1.1.0` до вашого проєкту та викликати її у своєму `auth.js` як `GithubStrategy`, це: `const GitHubStrategy = require('passport-github').Strategy;`. Не забудьте викликати та налаштовувати `dotenv` для використання змінних середовища.
Щоб налаштувати стратегію GitHub, ви повинні сказати Passport використовувати примірник `GitHubStrategy`, який приймає 2 аргументи: об'єкт (який містить `clientID`, `clientSecret` та `callbackURL`) та функцію, яку слід викликати після успішної автентифікації користувача, яка визначатиме, чи є користувач новим і які поля спочатку зберегти в об’єкті бази даних користувача. Це типово для багатьох стратегій, але деяким може знадобитися більше інформації, як зазначено в цій конкретній стратегії GitHub README. Наприклад, так Google вимагає *scope*, що визначає, яку інформацію просить повернути ваш запит, і просить користувача надати дозвіл на такий доступ. Використання поточної стратегії, яку ми впроваджуємо описане [тут](https://github.com/jaredhanson/passport-github/), але ми все це розглянемо тут, на freeCodeCamp!
Ось як ваша нова стратегія має виглядати на цьому етапі:
```js
passport.use(new GitHubStrategy({
clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: /*INSERT CALLBACK URL ENTERED INTO GITHUB HERE*/
},
function(accessToken, refreshToken, profile, cb) {
console.log(profile);
//Database logic here with callback containing our user object
}
));
```
Ваша автентифікація ще не є успішною, і насправді видасть помилку без логіки бази даних та зворотного виклику, але вона повинна зареєструвати ваш профіль GitHub на вашій консолі, якщо ви це спробуєте!
Підтвердьте сторінку, якщо все зрозуміло. Якщо виникла помилка, ви можете перевірити виконання проєкту до цього етапу [тут](https://gist.github.com/camperbot/ff3a1166684c1b184709ac0bee30dee6).
# --hints--
Необхідно додати залежність passport-github.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'passport-github',
'Your project should list "passport-github" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Слід вказати passport-github.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/auth.js').then(
(data) => {
assert.match(
data,
/require.*("|')passport-github("|')/gi,
'You should have required passport-github'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Стратегію GitHub слід правильно налаштувати.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/auth.js').then(
(data) => {
assert.match(
data,
/passport\.use.*new GitHubStrategy/gis,
'Passport should use a new GitHubStrategy'
);
assert.match(
data,
/callbackURL:\s*("|').*("|')/gi,
'You should have a callbackURL'
);
assert.match(
data,
/process\.env(\.GITHUB_CLIENT_SECRET|\[(?<q>"|')GITHUB_CLIENT_SECRET\k<q>\])/g,
'You should use process.env.GITHUB_CLIENT_SECRET'
);
assert.match(
data,
/process\.env(\.GITHUB_CLIENT_ID|\[(?<q>"|')GITHUB_CLIENT_ID\k<q>\])/g,
'You should use process.env.GITHUB_CLIENT_ID'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,80 @@
---
id: 589a8eb3f9fc0f352b528e72
title: Реалізація соціальної автентифікації ІІІ
challengeType: 2
forumTopicId: 301558
dashedName: implementation-of-social-authentication-iii
---
# --description--
Кінцева частина стратегії керує профілем поверненим з GitHub. Якщо існує об'єкт бази даних користувача, необхідно його завантажити. У разі відсутності, потрібно його створити. Потім заповнюємо поля профілю і повертаємо об'єкт користувача. У межах кожного профілю GitHub надає нам унікальний *id*, який можна використовувати для пошуку для того, щоб серіалізувати користувача (уже реалізовано). Нижче наведено приклад реалізації, який ви можете використовувати у вашому проєкті - це відбувається завдяки функції, яка є другим аргументом для нової стратегії, нижче де `console.log(profile);` наразі є:
```js
myDataBase.findOneAndUpdate(
{ id: profile.id },
{
$setOnInsert: {
id: profile.id,
name: profile.displayName || 'John Doe',
photo: profile.photos[0].value || '',
email: Array.isArray(profile.emails)
? profile.emails[0].value
: 'No public email',
created_on: new Date(),
provider: profile.provider || ''
},
$set: {
last_login: new Date()
},
$inc: {
login_count: 1
}
},
{ upsert: true, new: true },
(err, doc) => {
return cb(null, doc.value);
}
);
```
`findOneAndUpdate` дозволяє шукати та оновлювати об'єкт. У разі, якщо об'єкта не існує, його вставлять та зроблять доступним для функції зворотнього зв'язку. У цьому прикладі, ми завжди встановлюємо `last_login`, збільшуємо `login_count` на `1` та після додавання нового об'єкту (користувача) лише заповнюємо більшість полів. Зверніть увагу на використання значень за замовчуванням. Іноді повернений профіль не міститиме усієї інформації або ж користувач зробить її приватною. У такому випадку, ви його обробляєте задля уникнення помилки.
Ви повинні мати змогу увійти в ваш додаток прямо зараз — спробуйте!
Підтвердьте сторінку, якщо все виконано вірно. Якщо виникла помилка, ви можете перевірити виконання проєкту до цього етапу [тут](https://gist.github.com/camperbot/183e968f0e01d81dde015d45ba9d2745).
# --hints--
Ви повинні завершити налаштування стратегії GitHub.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/auth.js').then(
(data) => {
assert.match(
data,
/GitHubStrategy[^]*myDataBase/gi,
'Strategy should use now use the database to search for the user'
);
assert.match(
data,
/GitHubStrategy[^]*return cb/gi,
'Strategy should return the callback function "cb"'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,108 @@
---
id: 589a69f5f9fc0f352b528e70
title: Реалізація соціальної автентифікації
challengeType: 2
forumTopicId: 301559
dashedName: implementation-of-social-authentication
---
# --description--
Основний шлях автентифікації відстежуватиметься у вашому застосунку:
1. Користувач натисне кнопку або з'єднається з нашим маршрутом для автентифікації у визначеній стратегії (наприклад, GitHub).
2. Ваш маршрут `passport.authenticate('github')` перенаправляє їх на GitHub.
3. Сторінка, на яку користувач заходить, на GitHub, дозволяє йому увійти в систему, якщо він ще не зробив це. Система попросить підтвердити доступ до свого профілю з нашого застосунку.
4. Потім користувач повертається до нашого застосунку за адресою зворотного виклику url з його профілем, якщо він схвалений.
5. Тепер вони аутентифіковані, і ваш застосунок перевірить, чи це постійний профіль, або збереже його у вашій базі даних, якщо це не так.
Стратегії з OAuth потребують, щоб ви мали принаймні *Client ID* та *Client Secret*, так сервіс перевірить, від кого надходить запит автентифікації та чи є він дійсним. Вони отримані з сайту, з яким ви намагаєтеся здійснити автентифікацію, наприклад, з сайту GitHub і вони є унікальними для вашого застосунку **ВОНИ НЕ Є ДЛЯ ЗАГАЛЬНОГО ДОСТУПУ** і ніколи не повинні бути завантажені в публічне сховище або написані безпосередньо у вашому коді. Поширеною практикою є розміщення їх у файлі `.env` і посилання на них таким чином: `process.env.GITHUB_CLIENT_ID`. Для вирішення цього завдання ми будемо використовувати стратегію GitHub.
Отримання вашого *Client ID and Secret* від GitHub можна зробити в налаштуваннях профілю вашого облікового запису в розділі "Налаштування розробника", потім '[OAuth applications](https://github.com/settings/developers)'. Натисніть "Зареєструвати нову програму", назвіть свій додаток, вставте URL-адресу на свою домашню сторінку Replit (**Not the project code's url**), і, зрештою, для URL-адреси зворотного виклику, вставте ту саму URL-адресу як і домашню сторінку, але з додаванням `/auth/github/callback`. Сюди користувачі будуть переадресовані для обробки після автентифікації на GitHub. Збережіть отриману інформацію як `'GITHUB_CLIENT_ID'` та `'GITHUB_CLIENT_SECRET'` у вашому файлі `.env`.
У вашому файлі `routes.js` додайте `showSocialAuth: true` до маршруту головної сторінки після `showRegistration: true`. Тепер створіть 2 маршрути, які прийматимуть запити GET: `/auth/github` та `/auth/github/callback`. Перший повинен лише викликати паспорт для автентифікації `'github'`. Другий повинен викликати паспорт для автентифікації `'github'` з помилкою переадресації на `/`, а потім, якщо вдалося, переадресувати на `/profile` (подібно до нашого останнього проєкту).
Приклад того, що `/auth/github/callback` має виглядати подібним до обробки звичного логіну:
```js
app.route('/login')
.post(passport.authenticate('local', { failureRedirect: '/' }), (req,res) => {
res.redirect('/profile');
});
```
Підтвердьте сторінку, якщо все зрозуміло. Якщо сталась якась помилка, ви маєте змогу перевірити статус проєкту до цього етапу [тут](https://gist.github.com/camperbot/1f7f6f76adb178680246989612bea21e).
# --hints--
Шлях `/auth/github` має бути правильним.
```js
async (getUserInput) => {
try {
const res = await fetch(getUserInput('url') + '/_api/routes.js');
if (res.ok) {
const data = await res.text();
assert.match(
data.replace(/\s/g, ''),
/passport.authenticate.*?github/g,
'Route auth/github should only call passport.authenticate with github'
);
} else {
throw new Error(res.statusText);
}
const res2 = await fetch(getUserInput('url') + '/_api/app-stack');
if (res2.ok) {
const data2 = JSON.parse(await res2.json());
const dataLayer = data2.find(layer => layer?.route?.path === '/auth/github');
assert.deepInclude(dataLayer?.route, { methods: {get: true}, path: "/auth/github"});
assert.deepInclude(dataLayer?.route?.stack?.[0], {method: "get", name: "authenticate"});
} else {
throw new Error(res2.statusText);
}
} catch (err) {
throw new Error(err);
}
}
```
Шлях `/auth/github/callback` повинен бути правильним.
```js
async (getUserInput) => {
try {
const res = await fetch(getUserInput('url') + '/_api/routes.js');
if (res.ok) {
const data = await res.text();
assert.match(
data.replace(/\s/g, ''),
/failureRedirect:("|')\/\1/g,
'Route auth/github/callback should accept a get request and call passport.authenticate for github with a failure redirect to home'
);
} else {
throw new Error(res.statusText);
}
const res2 = await fetch(getUserInput('url') + '/_api/app-stack');
if (res2.ok) {
const data2 = JSON.parse(await res2.json());
const dataLayer = data2.find(layer => layer?.route?.path === '/auth/github/callback');
assert.deepInclude(dataLayer?.route, { methods: {get: true}, path: "/auth/github/callback"});
assert.deepInclude(dataLayer?.route?.stack?.[0], {method: "get", name: "authenticate"});
} else {
throw new Error(res2.statusText);
}
} catch (err) {
throw new Error(err);
}
}
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,81 @@
---
id: 58965611f9fc0f352b528e6c
title: Вихід з облікового запису
challengeType: 2
forumTopicId: 301560
dashedName: logging-a-user-out
---
# --description--
Логіка logout легко створюється. Маршрут має лише скасувати автентифікацію користувача та переадресувати на головну сторінку замість візуалізації будь-якого перегляду.
Скасувати автентифікацію користувача через Passport так само легко, як викликати `req.logout();` до початку переспрямування.
```js
app.route('/logout')
.get((req, res) => {
req.logout();
res.redirect('/');
});
```
Певно, ви помітили, що ми не обробляємо відсутні вебсторінки (404). Поширений спосіб обробки цієї помилки у Node є наступне проміжне програмне забезпечення. Додайте це після всіх інших маршрутів:
```js
app.use((req, res, next) => {
res.status(404)
.type('text')
.send('Not Found');
});
```
Підтвердьте сторінку, якщо все виконано вірно. Якщо виникла помилка, Ви можете перевірити виконаний до цього етапу проєкт [тут](https://gist.github.com/camperbot/c3eeb8a3ebf855e021fd0c044095a23b).
# --hints--
`req.Logout` має викликатися у маршруті `/logout`.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/req.logout/gi,
'You should be calling req.logout() in your /logout route'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Logout має переадресовувати на головну сторінку.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/logout').then(
(data) => {
assert.match(
data,
/Home page/gi,
'When a user logs out they should be redirected to the homepage'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,205 @@
---
id: 58966a17f9fc0f352b528e6d
title: Реєстрація нових користувачів
challengeType: 2
forumTopicId: 301561
dashedName: registration-of-new-users
---
# --description--
Тепер потрібно дозволити новому користувачеві створити обліковий запис на нашому сайті. У `res.render` для головної сторінки додайте нову змінну до переданого об'єкта `showRegistration: true`. Після оновлення вашої сторінки, ви побачите реєстраційну форму, яка вже була створена в файлі `index.pug`! Ця форма налаштована для надсилання **POST** запиту до `/register`, тому тут ми повинні прийняти **POST** і створити об'єкт користувача в базі даних.
Логіка реєстраційного маршруту має бути наступною: Реєстрація нового користувача > Автентифікація нового користувача > Переадресація на /profile
Логіка кроку 1, реєстрація нового користувача, повинна бути такою: Зробіть запит до бази даних за допомогою команди findOne > Якщо користувач був повернений, то він існує і переадресовується на головну сторінку *АБО* якщо користувач не визначений і помилки не виникло, тоді 'insertOne' у базу даних з ім'ям користувача та паролем, і поки не з'явиться помилок, викликайте *наступного*, щоб перейти до кроку 2, автентифікації нового користувача, для якого вже написана логіка в маршруті POST */login*.
```js
app.route('/register')
.post((req, res, next) => {
myDataBase.findOne({ username: req.body.username }, function(err, user) {
if (err) {
next(err);
} else if (user) {
res.redirect('/');
} else {
myDataBase.insertOne({
username: req.body.username,
password: req.body.password
},
(err, doc) => {
if (err) {
res.redirect('/');
} else {
// The inserted document is held within
// the ops property of the doc
next(null, doc.ops[0]);
}
}
)
}
})
},
passport.authenticate('local', { failureRedirect: '/' }),
(req, res, next) => {
res.redirect('/profile');
}
);
```
Підтвердьте сторінку, якщо все виконано вірно. Якщо сталась якась помилка, ви маєте змогу перевірити зроблений проєкт до цього етапу [тут](https://gist.github.com/camperbot/b230a5b3bbc89b1fa0ce32a2aa7b083e).
**ПРИМІТКА:** З цього моменту можуть виникнути проблеми з використанням браузеру *picture-in-picture*. Якщо ви використовуєте онлайн IDE, який має попередній перегляд програми в редакторі, рекомендується відкрити цей попередній перегляд у новій вкладці.
# --hints--
Ви повинні зареєструвати маршрут та відобразити його на головній сторінці.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/showRegistration:( |)true/gi,
'You should be passing the variable showRegistration as true to your render function for the homepage'
);
assert.match(
data,
/register[^]*post[^]*findOne[^]*username:( |)req.body.username/gi,
'You should have a route accepted a post request on register that querys the db with findone and the query being username: req.body.username'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Реєстрація повинна працювати.
```js
async (getUserInput) => {
const url = getUserInput('url');
const user = `freeCodeCampTester${Date.now()}`;
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
test(this);
} else {
throw new Error(`${this.status} ${this.statusText}`);
}
};
xhttp.open('POST', url + '/register', true);
xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhttp.send(`username=${user}&password=${user}`);
function test(xhttpRes) {
const data = xhttpRes.responseText;
assert.match(
data,
/Profile/gi,
'Register should work, and redirect successfully to the profile.'
);
}
};
```
Вхід в обліковий запис повинен працювати.
```js
async (getUserInput) => {
const url = getUserInput('url');
const user = `freeCodeCampTester${Date.now()}`;
const xhttpReg = new XMLHttpRequest();
xhttpReg.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
login();
} else {
throw new Error(`${this.status} ${this.statusText}`);
}
};
xhttpReg.open('POST', url + '/register', true);
xhttpReg.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhttpReg.send(`username=${user}&password=${user}`);
function login() {
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
test(this);
} else {
throw new Error(`${this.status} ${this.statusText}`);
}
};
xhttp.open('POST', url + '/login', true);
xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhttp.send(`username=${user}&password=${user}`);
}
function test(xhttpRes) {
const data = xhttpRes.responseText;
assert.match(
data,
/Profile/gi,
'Login should work if previous test was done successfully and redirect successfully to the profile.'
);
assert.match(
data,
new RegExp(user, 'g'),
'The profile should properly display the welcome to the user logged in'
);
}
};
```
Вихід з облікового запису повинен працювати.
```js
(getUserInput) =>
$.ajax({
url: getUserInput('url') + '/logout',
type: 'GET',
xhrFields: { withCredentials: true }
}).then(
(data) => {
assert.match(data, /Home/gi, 'Logout should redirect to home');
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Профіль більше не повинен працювати після виходу з облікового запису.
```js
(getUserInput) =>
$.ajax({
url: getUserInput('url') + '/profile',
type: 'GET',
crossDomain: true,
xhrFields: { withCredentials: true }
}).then(
(data) => {
assert.match(
data,
/Home/gi,
'Profile should redirect to home when we are logged out now again'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,79 @@
---
id: 589fc832f9fc0f352b528e79
title: Відправлення та відображення повідомлень чату
challengeType: 2
forumTopicId: 301562
dashedName: send-and-display-chat-messages
---
# --description--
Настав час почати надсилати повідомлення чату на сервер всім клієнтам! У файлі `client.js`, ви бачите, що вже існує блок обробки коду, після відправлення форми повідомлення.
```js
$('form').submit(function() {
/*logic*/
});
```
У межах коду відправлення форми ви повинні видати (emit) подію після визначення `messageToSend`, але перед тим, як ви очистите текстову панель `#m`. Код повинен називатися `'chat message'` і дані повинні бути `messageToSend`.
```js
socket.emit('chat message', messageToSend);
```
Тепер на вашому сервері, ви мусите прослухати сокет для події `'chat message'` з назвою `message`. Після отримання події він повинен буде видати подію `'chat message'` до всіх сокетів `io.emit` з даними об'єкта, що містить `name` та `message`.
В `client.js`, тепер необхідно послухати подію `'chat message'` і після отримання, додати список елементів до `#message` з іменем, двокрапкою та повідомленням!
На даний момент чат повинен бути повністю функціональним і спроможним відправляти повідомлення всім клієнтам!
Підтвердіть вашу сторінку, якщо все зрозуміло. Якщо сталась якась помилка, ви маєте змогу перевірити зроблений проєкт до цього етапу [тут](https://gist.github.com/camperbot/d7af9864375207e254f73262976d2016).
# --hints--
Сервер має слухати `'chat message'` та переміщувати (emit) його належним чином.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/socket.on.*('|")chat message('|")[^]*io.emit.*('|")chat message('|").*name.*message/gis,
'Your server should listen to the socket for "chat message" then emit to all users "chat message" with name and message in the data object'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Клієнт повинен правильно обробляти та показувати нові дані із події `'chat message'`.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/public/client.js').then(
(data) => {
assert.match(
data,
/socket.on.*('|")chat message('|")[^]*messages.*li/gis,
'You should append a list item to #messages on your client within the "chat message" event listener to display the new message'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,131 @@
---
id: 5895f70cf9fc0f352b528e66
title: Серіалізація об'єкта користувача
challengeType: 2
forumTopicId: 301563
dashedName: serialization-of-a-user-object
---
# --description--
Серіалізація та десеріалізація є важливими поняттями щодо автентифікації. Серіалізація об’єкта означає перетворення його вмісту в маленький *ключ*, який потім можна десеріалізувати у вихідний об’єкт. Це дозволяє нам знати, хто користувався сервером без надсилання даних автентифікації, таких як ім’я користувача та пароль, при кожному запиті на нову сторінку.
Щоб налаштувати його належним чином, потрібно мати функцію серіалізації та функцію десеріалізації. У Passport, ми створюємо їх за допомогою `passport.serializeUser( OURFUNCTION )` та `passport.deserializeUser( OURFUNCTION )`
`serializeUser` викликається 2 аргументами, повним об'єктом користувача та зворотним викликом, що використовується паспортом. Унікальний ключ для ідентифікації того, що користувача слід повернути у зворотному виклику, найпростіший у використанні `_id` користувача в об'єкті. Він повинен бути унікальним, оскільки він створений MongoDB. Аналогічно, `deserializeUser` викликається за допомогою цього ключа і також функції зворотного виклику паспорта, але цього разу ми повинні взяти цей ключ і повернути повний об'єкт користувача до зворотного виклику. Щоб здійснити пошук за запитом для Mongo `_id`, вам потрібно створити `const ObjectID = require('mongodb').ObjectID;`, а потім, щоб використати його, викличте `new ObjectID(THE_ID)`. Обов’язково додайте `mongodb@~3.6.0` як залежність. Ви можете побачити це в наведених нижче прикладах:
```js
passport.serializeUser((user, done) => {
done(null, user._id);
});
passport.deserializeUser((id, done) => {
myDataBase.findOne({ _id: new ObjectID(id) }, (err, doc) => {
done(null, null);
});
});
```
ПРИМІТКА: `deserializeUser` видасть помилку, поки ми не налаштуємо базу даних (DB) на наступному кроці, тому поки що прокоментуйте весь блок і просто викличте `done(null, null)` у функції `deserializeUser`.
Підтвердіть сторінку, якщо все виконано вірно. Якщо виникла помилка, ви можете перевірити виконання проєкту до цього етапу [here](https://gist.github.com/camperbot/7068a0d09e61ec7424572b366751f048).
# --hints--
Вам слід правильно серіалізувати функцію користувача.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/passport.serializeUser/gi,
'You should have created your passport.serializeUser function'
);
assert.match(
data,
/null,\s*user._id/gi,
'There should be a callback in your serializeUser with (null, user._id)'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Вам слід належним чином десеріалізувати функцію користувача.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/passport.deserializeUser/gi,
'You should have created your passport.deserializeUser function'
);
assert.match(
data,
/null,\s*null/gi,
'There should be a callback in your deserializeUser with (null, null) for now'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
MongoDB має бути залежністю.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'mongodb',
'Your project should list "mongodb" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Mongodb повинен бути включеним (required) належним чином, включаючи ObjectId.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/require.*("|')mongodb\1/gi,
'You should have required mongodb'
);
assert.match(
data,
/new ObjectID.*id/gi,
'Even though the block is commented out, you should use new ObjectID(id) for when we add the database'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,116 @@
---
id: 5895f700f9fc0f352b528e63
title: Налаштування шаблону двигуна
challengeType: 2
forumTopicId: 301564
dashedName: set-up-a-template-engine
---
# --description--
Робота над цими завданнями передбачає написання коду одним із таких методів:
- Клонуйте [ цей GitHub репозиторій](https://github.com/freeCodeCamp/boilerplate-advancednode/) і локально виконайте ці завдання.
- Використовуйте [наш стартовий проєкт Replit](https://replit.com/github/freeCodeCamp/boilerplate-advancednode) для виконання цих завдань.
- Використовуйте конструктор сайтів на ваш розсуд для завершення проекту. Впевніться, що ви маєте усі файли з нашого GitHub репозиторію.
По завершенню, переконайтеся, що демоверсія вашого проєкту розміщена у відкритому доступі. Потім введіть URL-адресу проєкту у поле `Solution Link`.
Шаблон двигуна дозволяє використовувати статичні шаблони файлів (такі як написані в *Pug*) у вашому додатку. У той час шаблон двигуна замінює варіації у файлі шаблону фактичними значеннями, які можуть надаватися вашим сервером. Потім він перетворює шаблон в статичний HTML файл, який надсилається клієнту. Цей підхід спрощує дизайн HTML сторінки та дозволяє показувати змінні на сторінці без необхідності виклику API від клієнта.
Додайте `pug@~3.0.0` в якості залежності у файлі `package.json`.
Висловіть свої потреби для розуміння, який шаблон двигуна ви використовуєте. Ми будемо використовувати метод `set` для призначення `pug` як `view engine` значення властивості: `app.set('view engine', 'pug')`
Ваша сторінка не буде завантажуватися, поки ви не зобразите правильно індексний файл у каталозі `views/pug`.
Змініть аргумент `res.render()` оголошення в маршруті `/` як шлях до файлу каталогу `views/pug`. Шлях може бути відносним шляхом (відносно переглядів) або абсолютним шляхом і не потребує розширення файлу.
Якщо все пройшло по плану, ваша домашня сторінка додатку зупинить показ повідомлення "`Pug template is not defined.`" і тепер буде показано повідомлення, яке вказує, що ви успішно зобразили шаблон Pug!
Підтвердіть свою сторінку, коли зрозумієте, що все працює коректно. Якщо виникла помилка, ви можете перевірити виконання проєкту до цього етапу [тут](https://gist.github.com/camperbot/3515cd676ea4dfceab4e322f59a37791).
# --hints--
Pug повинен бути залежністю.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'pug',
'Your project should list "pug" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Двигун перегляду має бути Pug.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/('|")view engine('|"),( |)('|")pug('|")/gi,
'Your project should set Pug as a view engine'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Використайте правильний метод ExpressJS, щоб відобразити сторінку індексу з відповіді.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/').then(
(data) => {
assert.match(
data,
/FCC Advanced Node and Express/gi,
'You successfully rendered the Pug template!'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Pug має працювати.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/').then(
(data) => {
assert.match(
data,
/pug-success-message/gi,
'Your projects home page should now be rendered by pug with the projects .pug file unaltered'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,132 @@
---
id: 5895f70cf9fc0f352b528e65
title: Налаштування Passport
challengeType: 2
forumTopicId: 301565
dashedName: set-up-passport
---
# --description--
Час встановити *Passport*, щоб нарешті дозволити користувачеві реєструватися або входити в обліковий запис! Разом з Passport, ми будемо використовувати експрес-сесію (Express-session) для обробки сесій. Використання цього проміжного програмного забезпечення зберігає id сесії як файл cookie в клієнті та дозволяє нам отримувати доступ до даних сесії за допомогою цього id на сервері. Таким чином ми зберігаємо особисту інформацію облікового запису поза файлом cookie, який клієнт використовує для перевірки на нашому сервері їх автентичності, і просто зберігаємо *ключ* для доступу до даних, що зберігаються на сервері.
Щоб налаштувати Passport для використання в своєму проекті, вам потрібно буде спочатку додати його як залежність у вашому package.json. `passport@~0.4.1`
Крім того, додайте також експрес-сесію як залежність. Експрес-сесія має дуже великий набір розширених функцій, які ви можете використати, але зараз ми збираємося використовувати лише базові функції! `express-session@~1.17.1`
Зараз вам потрібно буде встановити налаштування сесії та ініціалізувати Passport. Переконайтеся, що створили змінні 'session' та 'passport', це потрібно для 'express-session' та 'passport'.
Щоб налаштувати експрес-застосунок для використання у сесії, ми визначимо лише декілька основних параметрів. Не забудьте додати 'SESSION_SECRET' до вашого .env файлу і надати йому випадкове значення. Це використовується для обчислення хешу, яке використовується для шифрування вашого cookie!
```js
app.use(session({
secret: process.env.SESSION_SECRET,
resave: true,
saveUninitialized: true,
cookie: { secure: false }
}));
```
Крім того, ви можете продовжити та повідомити своєму експрес-застосунку **використовувати** 'Passpor.initialize()' та 'Pasort.session()'. (Наприклад, `app.use(passport.initialize());`)
Відправте сторінку, якщо все було виконано правильно. Якщо виникають помилки, ви можете перевірити виконання проєкту до цього етапу [тут](https://gist.github.com/camperbot/4068a7662a2f9f5d5011074397d6788c).
# --hints--
Passport і експрес-сесії повинні бути залежностями.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'passport',
'Your project should list "passport" as a dependency'
);
assert.property(
packJson.dependencies,
'express-session',
'Your project should list "express-session" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Пов'язані залежності повинні бути правильно заповнені.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/require.*("|')passport("|')/gi,
'You should have required passport'
);
assert.match(
data,
/require.*("|')express-session("|')/gi,
'You should have required express-session'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Експрес застосунок має використовувати нові залежності.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/passport.initialize/gi,
'Your express app should use "passport.initialize()"'
);
assert.match(
data,
/passport.session/gi,
'Your express app should use "passport.session()"'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Сесія і секрет сесії мають бути правильно налаштовані.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/secret *: *process\.env(\.SESSION_SECRET|\[(?<q>"|')SESSION_SECRET\k<q>\])/g,
'Your express app should have express-session set up with your secret as process.env.SESSION_SECRET'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,148 @@
---
id: 589fc830f9fc0f352b528e74
title: Налаштування середовища
challengeType: 2
forumTopicId: 301566
dashedName: set-up-the-environment
---
# --description--
Наступні завдання використовуватимуть файл `chat.pug`. Отже, у файлі `routes.js` додайте шлях GET, вказавши на `/chat`, що використовує `ensureAuthenticated` і відображає `chat.pug`, передаючи `{ user: req.user }` як аргумент до відповіді. Тепер змініть існуючий шлях `/auth/github/callback`, щоб встановити `req.session.user_id = req.user.id`, і перенаправте до `/chat`.
Додайте `socket.io@~2.3.0` як залежність і запросіть/встановіть її на свій сервер, визначивши наступним чином, із `http` (постачається вбудованою з Nodejs):
```javascript
const http = require('http').createServer(app);
const io = require('socket.io')(http);
```
Тепер, коли сервер *http* встановлений в *експрес застосунку*, чекайте відповіді від сервера *http*. Змініть рядок `app.listen` на `http.listen`.
Спершу необхідно прослухати нове з'єднання від клієнта. Ключове слово <dfn>on</dfn> робить лише це — прослуховує конкретну подію. Йому треба 2 аргумента: рядок, що містить заголовок події, що передається, і функція, якою проходять дані. У випадку нашого слухача зв'язку, скористаємось *socket*, щоб визначити дані в наступному аргументі. Сокет (socket) це підключений індивідуальний клієнт.
Щоб прослухати підключення до вашого сервера, додайте до вашого підключення бази даних наступне:
```javascript
io.on('connection', socket => {
console.log('A user has connected');
});
```
Тепер, щоб клієнт під'єднався, вам треба просто додати до вашого `client.js`, що завантажується сторінкою після автентифікації, наступне:
```js
/*global io*/
let socket = io();
```
Цей коментар замовчує помилку, яку ви б зазвичай бачили, оскільки 'io' не визначено в файлі. Ми вже додали надійну CDN до бібліотеки Socket.IO на сторінці chat.pug.
Тепер спробуйте завантажити свій додаток і автентифікуватись, і ви маєте побачити в консолі серверу напис 'A user has connected'!
**Примітка:**`io()` працює лише тоді коли підключений до сокета, який знаходиться на тому ж url/сервері. Щоб підключитись до зовнішнього сокета, який знаходиться в іншому місці, скористайтесь `io.connect('URL');`.
Підтвердіть свою сторінку, коли зрозумієте, що все працює коректно. Якщо ви натрапите на помилки, можете перевірити виконання проєкту до цього етапу [тут](https://gist.github.com/camperbot/aae41cf59debc1a4755c9a00ee3859d1).
# --hints--
`socket.io` має бути залежністю.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'socket.io',
'Your project should list "socket.io" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Ви маєте правильно запросити й встановити `http` як `http`.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/http.*=.*require.*('|")http\1/gi,
'Your project should list "http" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Ви маєте правильно запросити й встановити `socket.io` як `io`.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/io.*=.*require.*('|")socket.io\1.*http/gi,
'You should correctly require and instantiate socket.io as io.'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Socket.IO має прослуховувати з'єднання.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/io.on.*('|")connection\1.*socket/gi,
'io should listen for "connection" and socket should be the 2nd arguments variable'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Клієнт має бути підключеним до вашого сервера.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/public/client.js').then(
(data) => {
assert.match(
data,
/socket.*=.*io/gi,
'Your client should be connection to server with the connection defined as socket'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,53 @@
---
id: 5895f70bf9fc0f352b528e64
title: Використовуйте можливості шаблонного рушія
challengeType: 2
forumTopicId: 301567
dashedName: use-a-template-engines-powers
---
# --description--
Одна з головних переваг використання шаблонного рушія це можливість передавати змінні з сервера в файл шаблону перед його візуалізацією в HTML.
У вашому файлі Pug ви можете використовувати змінну, посилаючись на ім'я змінної як `#{variable_name}` в рядку з іншим текстом в елементі або використовуючи знак рівності в елементі без пробілу, наприклад, `p=variable_name`, що присвоює значення змінної тексту елемента p.
Ми рекомендуємо вивчити синтаксис і структуру Pug [тут](https://github.com/pugjs/pug) у README GitHub. Файл Pug націлений на використання прогалин і вкладок для зображення гніздових елементів і скорочення обсягу коду, необхідного для створення красивого сайту.
Коли йдеться про наш файл pug «index.pug», включений у ваш проєкт, ми використовували змінні *title* і *message*.
Щоб передати їх з нашого сервера, вам потрібно буде додати об'єкт як другий аргумент у ваш *res.render* зі змінними та їх значеннями. Наприклад, передайте цьому об'єкту установку змінних для індексного перегляду: `{title: 'Hello', message: 'Please login'}`
Це має мати наступний вигляд: `res.render(process.cwd() + '/views/pug/index', {title: 'Hello', message: 'Please login'});` Тепер оновіть свою сторінку. Під час перегляду ви повинні побачити ці значення у потрібному місці, як зазначено у файлі index.pug!
Якщо вам вдалося, запустіть сторінку. Якщо ви зіткнулися з помилками, можете перевірити завершений до цього етапу проєкт [тут](https://gist.github.com/camperbot/4af125119ed36e6e6a8bb920db0c0871).
# --hints--
Файл Pug повинен правильно відображати змінні.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/').then(
(data) => {
assert.match(
data,
/pug-variable("|')>Please login/gi,
'Your projects home page should now be rendered by pug with the projects .pug file unaltered'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```