chore(i18n,learn): processed translations (#44851)
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
---
|
||||
id: 589fc832f9fc0f352b528e78
|
||||
title: 新しいユーザーを通知する
|
||||
challengeType: 2
|
||||
forumTopicId: 301546
|
||||
dashedName: announce-new-users
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
多くのチャットルームは、ユーザーが接続または切断したときに、チャットにいる他のすべての接続中ユーザーにそのことを通知して表示することができます。 接続時と切断時にすでにイベントをエミットしているはずなので、このような機能をサポートするようにこのイベントを変更するだけです。 そのための最も論理的な方法として、イベントに伴う 3 つのデータを送信します。それらは、接続/切断したユーザーの名前、現在のユーザー数、および、その名前が接続されているか切断されているかを示すデータです。
|
||||
|
||||
イベント名を `'user'` に変更してください、そして、フィールド 「name」、「currentUsers」および「connected」(送信されたユーザーが接続する場合は `true`、切断する場合は `false`) を含むオブジェクトを渡してください。 必ず両方の「user count」イベントを変更し、切断イベントではフィールド 「connected」に対して、接続時にイベントが発生するような `true` ではなく `false` を送信するように設定してください。
|
||||
|
||||
```js
|
||||
io.emit('user', {
|
||||
name: socket.request.user.name,
|
||||
currentUsers,
|
||||
connected: true
|
||||
});
|
||||
```
|
||||
|
||||
これで必要なすべての情報がクライアントに揃い、ユーザーが接続または切断したときに現在のユーザー数と通知が正しく表示されます! クライアント側でこのイベントを処理するには、`'user'` をリッスンし、それから jQuery を使用して現在のユーザー数を更新し、`#num-users` のテキストを `'{NUMBER} users online'` に変更する必要があります。また、id が `messages` の順序なしリストに、`'{NAME} has {joined/left} the chat.'` という内容の `<li>` を追加します。
|
||||
|
||||
この実装は次のようになります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -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に対して、いくつかの設定を定義してインスタンス化した LocalStrategy オブジェクトを **use** するよう伝える必要があります。 このオブジェクト (および今後すべてのもの) はデータベース接続に依存するため、必ずデータベース接続でカプセル化してください!
|
||||
|
||||
```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 を正しく require して設定する必要があります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,154 @@
|
||||
---
|
||||
id: 589fc831f9fc0f352b528e77
|
||||
title: Socket.IOによる認証
|
||||
challengeType: 2
|
||||
forumTopicId: 301548
|
||||
dashedName: authentication-with-socket-io
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
今の時点で、誰がウェブソケットに接続しているかを判断することはできません。 `req.user` にはユーザーオブジェクトが含まれていますが、そうなっているのはユーザーがウェブサーバーとやり取りするときだけであり、ウェブソケットでは `req` (リクエスト) がないため、ユーザーデータはありません。 誰がウェブソケットに接続しているのかを知るための方法の 1 つとして、Passport セッションを含む Cookie を解析してデコードした後、デシリアライズしてユーザーオブジェクトを取得することができます。 幸い、 NPM にはまさにこのためのパッケージがあり、複雑な作業をシンプルにしてくれます!
|
||||
|
||||
`passport.socketio@~3.7.0`、`connect-mongo@~3.2.0` および `cookie-parser@~1.4.5` を依存関係として追加し、それらをそれぞれ `passportSocketIo`、`MongoStore` および `cookieParser` として require してください。 また、前に require した `express-session` から、新しいメモリストアを初期化する必要があります。 次のようになります。
|
||||
|
||||
```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
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
Socket.IO の Passport 認証の設定は、API の `session` ミドルウェアの設定方法によく似ています。 これは、同じ認証方式の使用を意図しているためです。つまり、Cookie からセッション id を取得して検証します。
|
||||
|
||||
前に `session` ミドルウェアを設定したときは、セッションの Cookie 名 (`key`) を明示的に設定してはいませんでした。 これは、`session` パッケージでデフォルト値を使用していたためです。 これで、Cookie から同じ値にアクセスする必要がある別のパッケージが追加されました。両方の設定オブジェクトへ明示的に `key` の値を設定する必要があります。
|
||||
|
||||
Socket.IO キーに一致する `session` ミドルウェアに、`key` と Cookie 名を追加してください。 また、`saveUninitialized: true` を設定した近くで、`store` 参照をオプションに追加してください。 これは、どのセッションに関連付けるかを 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 を適切に require する必要があります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,64 @@
|
||||
---
|
||||
id: 589690e6f9fc0f352b528e6e
|
||||
title: モジュールを使用してプロジェクトを整理する
|
||||
challengeType: 2
|
||||
forumTopicId: 301549
|
||||
dashedName: clean-up-your-project-with-modules
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
ここまで作成したものはすべて `server.js` ファイルにあります。 このままではコードが管理しずらくなって、あまり拡張できなくなる可能性があります。 `routes.js` と `auth.js` という 2 つの新しいファイルを作成してください。
|
||||
|
||||
どちらも次のコードから始める必要があります。
|
||||
|
||||
```js
|
||||
module.exports = function (app, myDataBase) {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
サーバーファイルの先頭で `const routes = require('./routes.js');` のようにしてファイルを require します。データベースとの接続を正常に確立した直後に、`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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,107 @@
|
||||
---
|
||||
id: 589fc831f9fc0f352b528e75
|
||||
title: エミットで通信する
|
||||
challengeType: 2
|
||||
forumTopicId: 301550
|
||||
dashedName: communicate-by-emitting
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
<dfn>エミット (emit)</dfn> は、最もよく使用する通信方法です。 サーバーから 「io」へ何かをエミットするときは、接続中のすべてのソケットにイベントの名前とデータを送信します。 エミットの良い例として、たとえば新しいユーザーが接続するたびに、接続したユーザーの現在の数をエミットすることができます。
|
||||
|
||||
まず、現在接続をリッスンしている場所の直前に、ユーザーを追跡する変数を追加してください。
|
||||
|
||||
```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」が表示されるはずです。 より多くのクライアントを読み込んで認証し、数字が増えるのを確認してください。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,87 @@
|
||||
---
|
||||
id: 5895f70df9fc0f352b528e6a
|
||||
title: 新しいミドルウェアを作成する
|
||||
challengeType: 2
|
||||
forumTopicId: 301551
|
||||
dashedName: create-new-middleware
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
通常、ユーザーは認証されていてもいなくても、 url を入力することで `/profile` にアクセスできます。 ここではこの動作を防ぐために、プロファイルページをレンダーする前に、ユーザーが認証されているかどうかを先に確認する必要があるとします。 こうした例に最適なのがミドルウェアです。
|
||||
|
||||
ここでのチャレンジは、ミドルウェア関数 `ensureAuthenticated(req, res, next)` を作成することです。この関数は、`request` 時に Passport の `isAuthenticated` メソッドを呼び出してユーザーが認証されているかどうかを確認します。メソッドでは、`req.user` が定義されているかどうかを確認します。 定義されている場合は、`next()` を呼び出します。定義されていない場合は、ホームページにリダイレクトすることでログインリクエストに応答します。 このミドルウェアの実装は次のようになります。
|
||||
|
||||
```js
|
||||
function ensureAuthenticated(req, res, next) {
|
||||
if (req.isAuthenticated()) {
|
||||
return next();
|
||||
}
|
||||
res.redirect('/');
|
||||
};
|
||||
```
|
||||
|
||||
次に、*ensureAuthenticated* を、プロファイルページのリクエストに対するミドルウェアとして、ページをレンダーする関数を含む get リクエスト引数の前に追加してください。
|
||||
|
||||
```js
|
||||
app
|
||||
.route('/profile')
|
||||
.get(ensureAuthenticated, (req,res) => {
|
||||
res.render(process.cwd() + '/views/pug/profile');
|
||||
});
|
||||
```
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/ae49b8778cab87e93284a91343da0959)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
ミドルウェア ensureAuthenticate を /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 への get リクエストを正しく / にリダイレクトする必要があります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: 589fc831f9fc0f352b528e76
|
||||
title: 切断を処理する
|
||||
challengeType: 2
|
||||
forumTopicId: 301552
|
||||
dashedName: handle-a-disconnect
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
お気づきかもしれませんが、ここまでは単にユーザー数を増やしているだけです。 ユーザーの切断処理も最初の接続と同じくらい簡単です。ただし、サーバー全体でリッスンするのではなくソケットごとにリッスンする必要があります。
|
||||
|
||||
これを行うには、データを渡さずにソケットで `'disconnect'` をリッスンする別のリスナーを、既存の `'connect'` リスナーの中に追加してください。 この機能をテストするには、ユーザーが切断したことをコンソールに記録するだけです。
|
||||
|
||||
```js
|
||||
socket.on('disconnect', () => {
|
||||
/*anything you want to do on disconnect*/
|
||||
});
|
||||
```
|
||||
|
||||
クライアントで現在のユーザーの更新されたカウントを継続的に保つようにするには、切断が発生したときに currentUsers を 1 減らし、それから、更新されたカウントで「user count」イベントをエミットする必要があります!
|
||||
|
||||
**注:** `'disconnect'` とまったく同様に、ソケットがサーバーへエミットできる他のすべてのイベントについても、「socket」を定義しているコネクションリスナーの中で処理する必要があります。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,86 @@
|
||||
---
|
||||
id: 58a25c98f9fc0f352b528e7f
|
||||
title: パスワードをハッシュする
|
||||
challengeType: 2
|
||||
forumTopicId: 301553
|
||||
dashedName: hashing-your-passwords
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
情報セキュリティのセクションに話を戻しますが、プレーンテキストのパスワードを保存することは*決して安全ではありません*。 ここでは、この問題を解決するために BCrypt を実装します。
|
||||
|
||||
`bcrypt@~5.0.0` を依存関係として追加し、サーバーで require してください。 2 つの重要な箇所でハッシュを処理する必要があります。1 つは、新しいアカウントの登録/保存を処理する箇所で、もう 1 つは、ログイン時にパスワードが正しいことを確認する箇所です。
|
||||
|
||||
現在の登録ルート上では、`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);
|
||||
}
|
||||
```
|
||||
|
||||
これで、パスワードを保存する必要がある場合に、最も重要なセキュリティ機能の1つを実装できます!
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](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 を正しく require して実装する必要があります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -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` の値を含むオブジェクトを、プロファイルビューのレンダーメソッドの 2 番目の引数として渡してください。 次に、`profile.pug` ビューに移動し、既存の `h1` 要素の下に同レベルのインデントで次の行を追加してください。
|
||||
|
||||
```pug
|
||||
h2.center#welcome Welcome, #{username}!
|
||||
```
|
||||
|
||||
これで、クラス「`center`」と id「`welcome`」を持ち、「`Welcome,`」の後にユーザー名を含む `h2` 要素が作成されます。
|
||||
|
||||
また、`profile.pug` で、ユーザーの認証解除のロジックを受け持つ `/logout` ルートを参照するリンクを追加してください。
|
||||
|
||||
```pug
|
||||
a(href='/logout') Logout
|
||||
```
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/136b3ad611cc80b41cab6f74bb460f6a)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
Pug render 変数を /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.
|
||||
*/
|
||||
```
|
@@ -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` としてオブジェクトに追加してください。 ページを更新すると、フォームが表示されます! このフォームは、`/login` で **POST** を実行するように設定されています。つまり、ここで POST を受け入れてユーザーを認証する必要があります。
|
||||
|
||||
このチャレンジでは、POST リクエストを受け入れるためにルート `/login` を追加する必要があります。 このルートで認証するには、レスポンスを送信する前にそのためのミドルウェアを追加する必要があります。 それには、レスポンスで `function(req,res)` の前に別のミドルウェアの引数を渡すだけです! 使用するミドルウェアは `passport.authenticate('local')` です。
|
||||
|
||||
`passport.authenticate` は引数として `{ failureRedirect: '/' }` などのいくつかのオプションを受け取ることができるのでとても便利です。ですので必ず追加してください。 ミドルウェア (認証ミドルウェアが成功した場合にのみ呼び出されます) を使用した後のレスポンスではユーザーを `/profile` にリダイレクトする必要があり、ルートでビュー `profile.pug` をレンダーする必要があります。
|
||||
|
||||
認証が成功すると、ユーザーオブジェクトが `req.user` に保存されます。
|
||||
|
||||
この時点で、ユーザー名とパスワードをフォームへ入力すると、ホームページ `/` にリダイレクトされ、サーバーのコンソールに `'User {USERNAME} attempted to log in.'` と表示されます。これは、今の段階では、登録されていないユーザーをログインさせないためです。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](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);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
/login への POST リクエストで / に正しくリダイレクトする必要があります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,91 @@
|
||||
---
|
||||
id: 5895f70cf9fc0f352b528e67
|
||||
title: Passport ユーザーのシリアライズを実装する
|
||||
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...
|
||||
```
|
||||
|
||||
`deserializeUser` の `myDataBase` コードを必ずコメント解除してください。そして `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);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
デシリアライズで DB を正しく使用している必要があり、`doc` を指定して `done(null, null)` を呼び出す必要があります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -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` として require する必要があります。たとえば `const GitHubStrategy = require('passport-github').Strategy;` のようになります。 環境変数を使用できるように、`dotenv` の require と設定を忘れないでください。
|
||||
|
||||
GitHub ストラテジーを設定するには、インスタンス化された `GitHubStrategy` を使用するよう Passport に指示する必要があります。これは 2 つの引数を受け取ります。1 つはオブジェクト (`clientID`、`clientSecret` および `callbackURL` を含む) で、もう 1 つはユーザーが正常に認証されたときに呼び出される関数です。この関数は、ユーザーが新規かどうかを判断し、ユーザーのデータベースオブジェクトに最初にどのフィールドを保存するかを決めます。 この手順は多くのストラテジーで一般的ですが、特定のストラテジーの GitHub README に概説されているように、より多くの情報が必要な場合もあります。 たとえば、Google では*スコープ*も必要です。スコープは、リクエストがどのような種類の情報を返すよう求めているのかを判断し、そのようなアクセスを承認するようユーザーに求めます。 ここで実装しているストラテジーの使用方法は[こちら](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 を require する必要があります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,80 @@
|
||||
---
|
||||
id: 589a8eb3f9fc0f352b528e72
|
||||
title: ソーシャル認証の実装Ⅲ
|
||||
challengeType: 2
|
||||
forumTopicId: 301558
|
||||
dashedName: implementation-of-social-authentication-iii
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
ストラテジーの最後の部分では、GitHub から返されたプロファイルを処理します。 ユーザーのデータベースオブジェクトが存在する場合はそれを読み込み、存在しない場合は作成する必要があります。 プロファイルからのフィールドを設定し、ユーザーのオブジェクトを返します。 GitHub からは固有の *id* が各プロファイル内に提供されます。この id を使用してユーザーを検索しシリアライズすることができます (すでに実装済み) 。 以下は、プロジェクトで使用できる実装の例です。新しいストラテジーの 2 番目の引数となる関数の中で、現在の `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.
|
||||
*/
|
||||
```
|
@@ -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 を使用したストラテジーでは、少なくとも*クライアント ID* と*クライアント シークレット*が必要です。サービスはこれらを使用して、認証リクエストが誰からのものか、またそれが有効かどうかを確認します。 これらは、認証を実装しようとしている GitHub などのサイトから取得され、アプリに固有のものです。これらの情報は**共有すべきではありません**。したがって、公開リポジトリにアップロードしたり、コード内に直接書き込んだりしないでください。 通常は、それらを `.env` ファイルに保存し、`process.env.GITHUB_CLIENT_ID` などのように参照します。 このチャレンジでは、GitHub ストラテジーを使用します。
|
||||
|
||||
GitHub からの*クライアント ID とシークレット*の取得は、「開発者設定」のアカウントプロファイル設定で実行され、その後は「[OAuth アプリケーション](https://github.com/settings/developers)」で実行されます。 「Register a new application (新しいアプリを登録する)」をクリックし、アプリに名前を付け、URL を Replit のホームページに貼り付けます (**プロジェクトコードの URL ではありません**)。最後に、コールバック URL をホームページと同じ URL に貼り付けますが、`/auth/github/callback` を追加します。 ユーザーはここにリダイレクトされ、GitHub で認証された後、処理が行われます。 返された情報を `'GITHUB_CLIENT_ID'` および `'GITHUB_CLIENT_SECRET'` として `.env` ファイルに保存します。
|
||||
|
||||
`routes.js` ファイルで、`showRegistration: true` の後に、`showSocialAuth: true` をホームページルートに追加します。 GET リクエストを受け付けるルートを 2 つ作成します。それらは、`/auth/github` と `/auth/github/callback` です。 1 つ目は、Passport を呼び出して `'github'` を認証するだけです。 2 つ目は、Passport を呼び出して `'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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,81 @@
|
||||
---
|
||||
id: 58965611f9fc0f352b528e6c
|
||||
title: ユーザーをログアウトさせる
|
||||
challengeType: 2
|
||||
forumTopicId: 301560
|
||||
dashedName: logging-a-user-out
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
ログアウトロジックの作成は簡単です。 ルートで、ユーザーの認証を解除し、ビューをレンダーするのではなくホームページにリダイレクトするだけです。
|
||||
|
||||
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);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
ログアウトからホームページにリダイレクトする必要があります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,205 @@
|
||||
---
|
||||
id: 58966a17f9fc0f352b528e6d
|
||||
title: 新規ユーザーの登録
|
||||
challengeType: 2
|
||||
forumTopicId: 301561
|
||||
dashedName: registration-of-new-users
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
新しいユーザーがサイトでアカウントを登録することを許可する必要があります。 ホームページの `res.render` で、渡されたオブジェクトに新しい変数 `showRegistration: true` を追加してください。 ページを更新すると、すでに `index.pug` ファイルで作成した登録フォームが表示されます! このフォームは `/register` の **POST** を実行するように設定されています。つまり、ここで **POST** を受け付けてデータベースにユーザーオブジェクトを作成します。
|
||||
|
||||
登録ルートのロジックは、「新規ユーザーの登録 > 新規ユーザーの認証 > /profileへのリダイレクト」になります。
|
||||
|
||||
ステップ 1 の新規ユーザーの登録のロジックでは、findOne コマンド > を使用してデータベースクエリを実行します。ユーザーが返された場合は、そのユーザーが存在しているためホームにリダイレクトします。 *または*、ユーザーが未定義でエラーが発生しなかった場合は、ユーザー名とパスワードを使用してデータベース に「insertOne」し、エラーが発生しない限り、 *next* を呼び出してステップ 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)で確認できます。
|
||||
|
||||
**注:** これ以降、*ピクチャー・イン・ピクチャー*対応ブラウザーの使用に関連する問題が生じる可能性があります。 エディター内でアプリのプレビューができるオンライン 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.
|
||||
*/
|
||||
```
|
@@ -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*/
|
||||
});
|
||||
```
|
||||
|
||||
フォームの送信コードの中で、`messageToSend` を定義した後、ただしテキストボックス `#m` をクリアする前に、イベントをエミットする必要があります。 イベント名は `'chat message'` にし、データは `messageToSend` にする必要があります。
|
||||
|
||||
```js
|
||||
socket.emit('chat message', messageToSend);
|
||||
```
|
||||
|
||||
次に、サーバー上でソケットをリッスンし、 `message` というデータを持つイベント `'chat message'` を受信する必要があります。 サーバーでイベントを受信したら、`name` と `message` を含むオブジェクトデータを使用して、イベント `'chat message'` をすべてのソケット `io.emit` にエミットする必要があります。
|
||||
|
||||
`client.js` では、イベント `'chat message'` をリッスンし、イベントを受信したら、名前、コロン、メッセージを使用してリストアイテムを `#messages` の末尾に追加する必要があります。
|
||||
|
||||
この時点でチャットが完全に機能し、すべてのクライアントにメッセージが送信されるはずです!
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/d7af9864375207e254f73262976d2016)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
サーバーは、`'chat message'` をリッスンして正しくエミットする必要があります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -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 つの引数を指定して呼び出します。1 つ目は完全なユーザーオブジェクトで、2 つ目は Passport で使用されるコールバックです。 コールバックからは、ユーザーを特定するための一意のキーを返す必要があります。オブジェクトのユーザー `_id` を使用するのが最も簡単です。 この _id は MongoDB によって生成されるため、一意になります。 同様に、`deserializeUser` もそのキーと Passport のコールバック関数を指定して呼び出しますが、ここでは、前述のキーを受け取り、完全なユーザー オブジェクトをコールバックに返す必要があります。 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 を設定するまではエラーをスローします。そのため、ここではブロック全体をコメントアウトして、関数 `deserializeUser` で `done(null, null)` を呼び出すだけにしてください。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](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);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
ObjectId を含めて Mongodb を正しく require する必要があります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,116 @@
|
||||
---
|
||||
id: 5895f700f9fc0f352b528e63
|
||||
title: テンプレートエンジンを設定する
|
||||
challengeType: 2
|
||||
forumTopicId: 301564
|
||||
dashedName: set-up-a-template-engine
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
チャレンジに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub レポジトリ](https://github.com/freeCodeCamp/boilerplate-advancednode/)をクローンし、ローカル環境でチャレンジを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-advancednode)を使用してチャレンジを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。
|
||||
|
||||
テンプレートエンジンを使用すると、(*Pug* で記述するような) 静的なテンプレートファイルをアプリで使用できます。 実行時にテンプレートエンジンによって、テンプレートファイル内の変数がサーバーから提供される実際の値に置き換えられます。 そして、テンプレートが静的な HTML ファイルに変換され、クライアントへ送信されます。 このアプローチにより、HTML ページの設計が容易になり、クライアントから API 呼び出しを行うことなく、ページに変数を表示することができます。
|
||||
|
||||
`pug@~3.0.0` を `package.json` ファイルに依存関係として追加してください。
|
||||
|
||||
どのテンプレートエンジンを使用しているかを Express に知らせる必要があります。 `set` メソッドを使用して、`view engine` プロパティの値として `pug` を割り当ててください (`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.
|
||||
*/
|
||||
```
|
@@ -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 を使用しますが、この方法によって個人のアカウント情報が Cookie に入り込むのを防ぎ、*キー*を通じてのみサーバーに保存されているデータにアクセスするようになります。
|
||||
|
||||
Passport をプロジェクトで使用できるように設定するには、まず package.json に依存関係として追加する必要があります。 `passport@~0.4.1`
|
||||
|
||||
また、express-session を依存関係として追加してください。 express-session はたくさんの高度な機能を備えていますが、ここでは基本的な機能のみを使用します。 `express-session@~1.17.1`
|
||||
|
||||
ここでセッション設定を行い、Passport を初期化する必要があります。 最初に変数 「session」と「passport」を作成し、「express-session」と「passport」をそれぞれ require してください。
|
||||
|
||||
セッションを使用するように Express アプリを設定するため、いくつかの基本的なオプションを定義します。 必ず.envファイルに「SESSION_SECRET」を追加し、ランダムな値を指定してください。 この値を使用して、Cookie の暗号化に使用されるハッシュを計算します!
|
||||
|
||||
```js
|
||||
app.use(session({
|
||||
secret: process.env.SESSION_SECRET,
|
||||
resave: true,
|
||||
saveUninitialized: true,
|
||||
cookie: { secure: false }
|
||||
}));
|
||||
```
|
||||
|
||||
また、Express アプリに「passport.initialize()」と「passport.session()」を **use** するよう指示してください (たとえば、`app.use(passport.initialize());` などとします)。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/4068a7662a2f9f5d5011074397d6788c)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
Passport と express-session を依存関係にする必要があります。
|
||||
|
||||
```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);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
依存関係を正しく require する必要があります。
|
||||
|
||||
```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);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Express アプリで新しい依存関係を使用する必要があります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,148 @@
|
||||
---
|
||||
id: 589fc830f9fc0f352b528e74
|
||||
title: 環境を設定する
|
||||
challengeType: 2
|
||||
forumTopicId: 301566
|
||||
dashedName: set-up-the-environment
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
以降のチャレンジでは `chat.pug` ファイルを使用します。 そこで、`routes.js` ファイルで、`/chat` を指す GET ルートを追加してください。/chat は、`ensureAuthenticated` を利用し、レスポンスへの引数として渡された `{ user: req.user }` を使用して `chat.pug` をレンダーします。 次に、既存の `/auth/github/callback` ルートを変更して、`req.session.user_id = req.user.id` を設定し、`/chat` にリダイレクトしてください。
|
||||
|
||||
`socket.io@~2.3.0` を依存関係として追加し、(Node.js に組み込まれている) `http`を使用して、以下のようにサーバーでソケットの require を定義しインスタンス化してください。
|
||||
|
||||
```javascript
|
||||
const http = require('http').createServer(app);
|
||||
const io = require('socket.io')(http);
|
||||
```
|
||||
|
||||
*http* サーバーを *Express アプリ*にマウントしたので、*http* サーバーからリッスンする必要があります。 `app.listen` の行を `http.listen` に変更します。
|
||||
|
||||
最初の処理として、クライアントからの新しい接続をリッスンします。 <dfn>on</dfn> キーワードがその処理を行い、特定のイベントがないかリッスンします。 ここでは、エミットされたイベントのタイトルを含む文字列と、データを渡すための関数の、2 つの引数が必要です。 コネクションリスナーの場合は、*ソケット*を使用して第 2 引数でデータを定義します。 ソケットとは、接続している個々のクライアントのことです。
|
||||
|
||||
サーバーへの接続をリッスンするには、データベース接続の中に次を追加します。
|
||||
|
||||
```javascript
|
||||
io.on('connection', socket => {
|
||||
console.log('A user has connected');
|
||||
});
|
||||
```
|
||||
|
||||
クライアントが接続できるようにするには、認証後にページによってロードされる `client.js` に次を追加する必要があります。
|
||||
|
||||
```js
|
||||
/*global io*/
|
||||
let socket = io();
|
||||
```
|
||||
|
||||
コメントにより、通常「io」がファイル内で定義されていない場合に表示されるエラーを表示しないようにしています。 chat.pug 内のページの Socket.IO ライブラリには、すでに信頼性の高い CDN を追加してあります。
|
||||
|
||||
では、アプリの読み込みと認証を試してみましょう。サーバーコンソールに「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` として正しく require しインスタンス化する必要があります。
|
||||
|
||||
```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` として正しく require しインスタンス化する必要があります。
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: 5895f70bf9fc0f352b528e64
|
||||
title: テンプレートエンジンのパワーを活用する
|
||||
challengeType: 2
|
||||
forumTopicId: 301567
|
||||
dashedName: use-a-template-engines-powers
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
テンプレートエンジンを使用する最大の利点の 1 つは、HTML にレンダーする前に変数をサーバーからテンプレートファイルに渡せることです。
|
||||
|
||||
Pug ファイルでは変数を使用することができます。それには、変数名を `#{variable_name}` として参照し、要素上の他のテキストとともにインラインで記述します。または、要素に対して `p=variable_name` のように空白を付けずに等号を使用します (この例では変数の値が p 要素のテキストに割り当てられます)。
|
||||
|
||||
Pug のシンタックスと構造については、[こちら](https://github.com/pugjs/pug)の GitHub README をぜひご覧してください。 Pug は、空白とタブを使用してネストされた要素を示し、少ないコード量で美しいサイトを作成することを目標としています。
|
||||
|
||||
プロジェクトに含まれている pug ファイル「index.pug」では、変数 *title* と *message* を使用しました。
|
||||
|
||||
これらをサーバーから渡すには、*res.render* への 2 番目の引数として、変数とその値を持つオブジェクトを追加する必要があります。 たとえば、インデックスビューの変数を設定するために、`{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.
|
||||
*/
|
||||
```
|
Reference in New Issue
Block a user