chore(i8n,curriculum): processed translations (#41551)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 589fc832f9fc0f352b528e78
|
||||
title: Announce New Users
|
||||
title: 用户公告
|
||||
challengeType: 2
|
||||
forumTopicId: 301546
|
||||
dashedName: announce-new-users
|
||||
@@ -8,9 +8,9 @@ dashedName: announce-new-users
|
||||
|
||||
# --description--
|
||||
|
||||
Many chat rooms are able to announce when a user connects or disconnects and then display that to all of the connected users in the chat. Seeing as though you already are emitting an event on connect and disconnect, you will just have to modify this event to support such a feature. The most logical way of doing so is sending 3 pieces of data with the event: the name of the user who connected/disconnected, the current user count, and if that name connected or disconnected.
|
||||
许多聊天室都有这个功能:所有已连接到服务器的在线用户都会看到有人加入或退出的提醒。 我们已经写好了处理连接和断开事件的代码,只要对这个方法稍作修改就可以实现这个功能。 在事件中,我们需要发送这三条信息:连接或断开的用户名、当前用户数量、事件类型(即需要知道用户究竟是连接还是断开)。
|
||||
|
||||
Change the event name to `'user'`, and pass an object along containing the fields 'name', 'currentUsers', and 'connected' (to be `true` in case of connection, or `false` for disconnection of the user sent). Be sure to change both 'user count' events and set the disconnect one to send `false` for the field 'connected' instead of `true` like the event emitted on connect.
|
||||
请将事件名称更改为 `'user'`,其中应包含如下字段:'name'、'currentUsers'、'connected'(布尔值,连接上即为 `true`,断开则是 `false`)。 请务必更改两个 user count 事件,设置 disconnect 事件向 connected 字段发送 `false` ,而不是像 connect 事件一样发送 `true`。
|
||||
|
||||
```js
|
||||
io.emit('user', {
|
||||
@@ -20,9 +20,9 @@ io.emit('user', {
|
||||
});
|
||||
```
|
||||
|
||||
Now your client will have all the necessary information to correctly display the current user count and announce when a user connects or disconnects! To handle this event on the client side we should listen for `'user'`, then update the current user count by using jQuery to change the text of `#num-users` to `'{NUMBER} users online'`, as well as append a `<li>` to the unordered list with id `messages` with `'{NAME} has {joined/left} the chat.'`.
|
||||
现在客户端已具备足够的信息,来显示当前用户数,并在用户连接或断开连接时通知! 接下来我们需要在客户端监听 `'user'` 事件,然后用 jQuery 把 `#num-users` 节点的文本内容更新为 `'{NUMBER} users online'`。 同时,我们需要为无序列表添加一个 id 为 `messages` 且带有 `'{NAME} has {joined/left} the chat.'` 文本的 `<li>`。
|
||||
|
||||
An implementation of this could look like the following:
|
||||
实现如下:
|
||||
|
||||
```js
|
||||
socket.on('user', data => {
|
||||
@@ -34,11 +34,11 @@ socket.on('user', data => {
|
||||
});
|
||||
```
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/bf95a0f74b756cf0771cd62c087b8286).
|
||||
完成上述要求后,你可以在下方提交你的页面链接。 如果你遇到了问题,可以参考 [这里](https://gist.github.com/camperbot/bf95a0f74b756cf0771cd62c087b8286) 的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
Event `'user'` should be emitted with name, currentUsers, and connected.
|
||||
事件 `'user'` 应该包含,name、 currentUsers 和 connected。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -56,7 +56,7 @@ Event `'user'` should be emitted with name, currentUsers, and connected.
|
||||
);
|
||||
```
|
||||
|
||||
Client should properly handle and display the new data from event `'user'`.
|
||||
客户端应处理和显示 `'user'` 中的新数据。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5895f70df9fc0f352b528e68
|
||||
title: Authentication Strategies
|
||||
title: 身份验证策略
|
||||
challengeType: 2
|
||||
forumTopicId: 301547
|
||||
dashedName: authentication-strategies
|
||||
@@ -8,11 +8,11 @@ dashedName: authentication-strategies
|
||||
|
||||
# --description--
|
||||
|
||||
A strategy is a way of authenticating a user. You can use a strategy for allowing users to authenticate based on locally saved information (if you have them register first) or from a variety of providers such as Google or GitHub. For this project, we will set up a local strategy. To see a list of the hundreds of strategies, visit Passport's site [here](http://passportjs.org/).
|
||||
策略是认证用户的一种方式。 如果你让用户在注册时填写了用户信息,那你就可以基于这些信息进行验证。或者也可以引入第三方登录,如 Google 或者 Github。 对于这个项目的验证策略,我们会采用自己搭建的方式完成。 可以[点击这里](http://passportjs.org/)访问 Passport 网站,查看数以百计的策略。
|
||||
|
||||
Add `passport-local` as a dependency and add it to your server as follows: `const LocalStrategy = require('passport-local');`
|
||||
引入 `passport-local` 作为依赖,然后将它添加到服务器,就像这样:`const LocalStrategy = require('passport-local');`。
|
||||
|
||||
Now you will have to tell passport to **use** an instantiated LocalStrategy object with a few settings defined. Make sure this (as well as everything from this point on) is encapsulated in the database connection since it relies on it!
|
||||
然后,需要让 passport **使用**一个实例化的 LocalStrategy 对象,这个对象的一些设置已完成。 请注意,接下来的所有代码都应写在连接数据库的回调中,因为它们的执行都依赖数据库。
|
||||
|
||||
```js
|
||||
passport.use(new LocalStrategy(
|
||||
@@ -28,17 +28,17 @@ passport.use(new LocalStrategy(
|
||||
));
|
||||
```
|
||||
|
||||
This is defining the process to use when we try to authenticate someone locally. First, it tries to find a user in our database with the username entered, then it checks for the password to match, then finally, if no errors have popped up that we checked for, like an incorrect password, the `user`'s object is returned and they are authenticated.
|
||||
这就是我们的用户验证逻辑: 首先根据用户输入的用户名在数据库中寻找用户;然后检查密码是否匹配,最后如果没有发生错误(比如密码错误),那么就会返回 `user` 对象并通过验证。
|
||||
|
||||
Many strategies are set up using different settings, but generally it is easy to set it up based on the README in that strategy's repository. A good example of this is the GitHub strategy where we don't need to worry about a username or password because the user will be sent to GitHub's auth page to authenticate. As long as they are logged in and agree then GitHub returns their profile for us to use.
|
||||
很多策略的配置都不同,但是,一般来说,根据策略仓库中的 README 来进行配置就足够了。 一个很好的例子是 GitHub 策略。在该策略中,我们不需要写用户名或密码的相关验证逻辑,因为用户将被引导到 GitHub 页面进行验证。 只要用户登录并同意,GitHub 就会返回用户的个人信息供我们使用。
|
||||
|
||||
In the next step, we will set up how to actually call the authentication strategy to validate a user based on form data!
|
||||
在下一个挑战中,我们会基于表单数据调用上面写好的验证策略。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/53b495c02b92adeee0aa1bd3f3be8a4b).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到任何问题,可以参考[这里](https://gist.github.com/camperbot/53b495c02b92adeee0aa1bd3f3be8a4b)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
Passport-local should be a dependency.
|
||||
需要使用 passport-local 作为依赖。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -57,7 +57,7 @@ Passport-local should be a dependency.
|
||||
);
|
||||
```
|
||||
|
||||
Passport-local should be correctly required and setup.
|
||||
应该正确地引入和配置 passport-local。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 589fc831f9fc0f352b528e77
|
||||
title: Authentication with Socket.IO
|
||||
title: 使用 Socket.IO 进行身份验证
|
||||
challengeType: 2
|
||||
forumTopicId: 301548
|
||||
dashedName: authentication-with-socket-io
|
||||
@@ -8,9 +8,9 @@ dashedName: authentication-with-socket-io
|
||||
|
||||
# --description--
|
||||
|
||||
Currently, you cannot determine who is connected to your web socket. While `req.user` contains the user object, that's only when your user interacts with the web server, and with web sockets you have no `req` (request) and therefore no user data. One way to solve the problem of knowing who is connected to your web socket is by parsing and decoding the cookie that contains the passport session then deserializing it to obtain the user object. Luckily, there is a package on NPM just for this that turns a once complex task into something simple!
|
||||
目前,你还无法确定连接到服务器的用户身份。 虽然 `req.user` 包含用户信息,但这个只在用户直接与服务器交互时产生。当用户通过 web socket 与服务器连接时,由于不存在 `req` 对象,我们就无法获取用户数据。 解决这个问题的方法之一是通过读取和解析请求中包含 passport session 的 cookie,然后反序列化,进而获取用户信息对象。 幸运的是,NPM 上有可以让这个复杂的流程简单化的库。
|
||||
|
||||
Add `passport.socketio`, `connect-mongo`, and `cookie-parser` as dependencies and require them as `passportSocketIo`, `MongoStore`, and `cookieParser` respectively. Also, we need to initialize a new memory store, from `express-session` which we previously required. It should look like this:
|
||||
添加 `passport.socketio`、`connect-mongo@~3.2.0`、`cookie-parser` 作为依赖,把它们分别赋值给 `passportSocketIo`、`MongoStore`、`cookieParser`。 同时,我们需要从之前引入的 `express-session` 中开辟新的内存空间, 就像这样:
|
||||
|
||||
```js
|
||||
const MongoStore = require('connect-mongo')(session);
|
||||
@@ -18,7 +18,7 @@ const URI = process.env.MONGO_URI;
|
||||
const store = new MongoStore({ url: URI });
|
||||
```
|
||||
|
||||
Now we just have to tell Socket.IO to use it and set the options. Be sure this is added before the existing socket code and not in the existing connection listener. For your server, it should look like this:
|
||||
现在我们只需要让 Socket.IO 调用它并进行相应的配置即可。 请注意,以上这些都必须放在现有的 socket 相关代码之前,而且不能放到连接的监听回调函数里。 你的服务器代码应类似于这样:
|
||||
|
||||
```js
|
||||
io.use(
|
||||
@@ -33,11 +33,11 @@ io.use(
|
||||
);
|
||||
```
|
||||
|
||||
Be sure to add the `key` and `store` to the `session` middleware mounted on the app. This is necessary to tell *SocketIO* which session to relate to.
|
||||
记得要把 `key` 和 `store` 加到 app 的 `session` 中间件。 这样,*SocketIO* 才知道该对哪个 session 执行此配置。
|
||||
|
||||
<hr />
|
||||
|
||||
Now, define the `success`, and `fail` callback functions:
|
||||
接下来,我们可以定义 `success` 与 `fail` 的回调函数:
|
||||
|
||||
```js
|
||||
function onAuthorizeSuccess(data, accept) {
|
||||
@@ -53,19 +53,19 @@ function onAuthorizeFail(data, message, error, accept) {
|
||||
}
|
||||
```
|
||||
|
||||
The user object is now accessible on your socket object as `socket.request.user`. For example, now you can add the following:
|
||||
现在,我们可以通过 `socket.request.user` 访问用户数据。 为此,你可以这样做:
|
||||
|
||||
```js
|
||||
console.log('user ' + socket.request.user.name + ' connected');
|
||||
```
|
||||
|
||||
It will log to the server console who has connected!
|
||||
这样,我们可以在 console 里看到谁连接到了我们的服务器。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project up to this point [here](https://gist.github.com/camperbot/1414cc9433044e306dd7fd0caa1c6254).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/1414cc9433044e306dd7fd0caa1c6254)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
`passport.socketio` should be a dependency.
|
||||
应添加 `passport.socketio` 作为依赖。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -84,7 +84,7 @@ Submit your page when you think you've got it right. If you're running into erro
|
||||
);
|
||||
```
|
||||
|
||||
`cookie-parser` should be a dependency.
|
||||
应添加 `cookie-parser` 作为依赖。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -103,7 +103,7 @@ Submit your page when you think you've got it right. If you're running into erro
|
||||
);
|
||||
```
|
||||
|
||||
passportSocketIo should be properly required.
|
||||
应正确引入 passportSocketIo。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -121,7 +121,7 @@ passportSocketIo should be properly required.
|
||||
);
|
||||
```
|
||||
|
||||
passportSocketIo should be properly setup.
|
||||
应正确配置 passportSocketIo。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 589690e6f9fc0f352b528e6e
|
||||
title: Clean Up Your Project with Modules
|
||||
title: 使用模块清理项目
|
||||
challengeType: 2
|
||||
forumTopicId: 301549
|
||||
dashedName: clean-up-your-project-with-modules
|
||||
@@ -8,9 +8,9 @@ dashedName: clean-up-your-project-with-modules
|
||||
|
||||
# --description--
|
||||
|
||||
Right now, everything you have is in your `server.js` file. This can lead to hard to manage code that isn't very expandable. Create 2 new files: `routes.js` and `auth.js`
|
||||
目前,我们把所有的代码都放到了 `server.js` 文件里, 这会导致代码难以维护,且扩展性差。 现在让我们来创建两个新文件:`routes.js` 和 `auth.js`。
|
||||
|
||||
Both should start with the following code:
|
||||
在每个文件的开头,我们都需要写上这段代码:
|
||||
|
||||
```js
|
||||
module.exports = function (app, myDataBase) {
|
||||
@@ -18,19 +18,19 @@ module.exports = function (app, myDataBase) {
|
||||
}
|
||||
```
|
||||
|
||||
Now, in the top of your server file, require these files like so: `const routes = require('./routes.js');` Right after you establish a successful connection with the database, instantiate each of them like so: `routes(app, myDataBase)`
|
||||
然后,在 server.js 文件的开头,像这样引入文件:`const routes = require('./routes.js');`。在成功连接数据库之后,像这样进行实例化:`routes(app, myDataBase)`。
|
||||
|
||||
Finally, take all of the routes in your server and paste them into your new files, and remove them from your server file. Also take the `ensureAuthenticated` function, since it was specifically created for routing. Now, you will have to correctly add the dependencies in which are used, such as `const passport = require('passport');`, at the very top, above the export line in your `routes.js` file.
|
||||
最后,把所有路由相关的代码从 server.js 移动到新文件。 不要忘了,`ensureAuthenticated` 方法的定义也要移动到新文件中,这个是我们在之前的挑战中,为在路由中判断用户是否已登录创建的函数。 然后,在 `routes.js`文件开头添加所需要的依赖,如:`const passport = require('passport');`。
|
||||
|
||||
Keep adding them until no more errors exist, and your server file no longer has any routing (**except for the route in the catch block**)!
|
||||
如果在这些步骤后没有报错,那么你已成功地从 server.js 文件中分离出了路由文件(**除了 catch block 中的路由**)!
|
||||
|
||||
Now do the same thing in your auth.js file with all of the things related to authentication such as the serialization and the setting up of the local strategy and erase them from your server file. Be sure to add the dependencies in and call `auth(app, myDataBase)` in the server in the same spot.
|
||||
现在,我们来把 server.js 中与验证相关的代码分离到 auth.js 中,例如序列化,设置验证策略等。 请正确添加依赖,并在 server.js 中调用 `auth(app, myDataBase)`。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out an example of the completed project [here](https://gist.github.com/camperbot/2d06ac5c7d850d8cf073d2c2c794cc92).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/2d06ac5c7d850d8cf073d2c2c794cc92)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
Modules should be present.
|
||||
应该有模块。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 589fc831f9fc0f352b528e75
|
||||
title: Communicate by Emitting
|
||||
title: 通过 Emitting 通信
|
||||
challengeType: 2
|
||||
forumTopicId: 301550
|
||||
dashedName: communicate-by-emitting
|
||||
@@ -8,27 +8,27 @@ dashedName: communicate-by-emitting
|
||||
|
||||
# --description--
|
||||
|
||||
<dfn>Emit</dfn> is the most common way of communicating you will use. When you emit something from the server to 'io', you send an event's name and data to all the connected sockets. A good example of this concept would be emitting the current count of connected users each time a new user connects!
|
||||
<dfn>Emit</dfn> 是你会用到的最常见的通信方式。 如果我们从服务器发送信息给 “io”,就相当于把事件的名称和数据发送给了所有处于连接状态的 socket。 我们可以利用这个特性实现这样的功能:只要有新用户连接到服务器,我们就可以把目前连接的总用户数发给所有已连接的用户,这样所有用户随时都可以看到实时的在线人数。
|
||||
|
||||
Start by adding a variable to keep track of the users, just before where you are currently listening for connections.
|
||||
首先,我们需要在监听连接的地方之前添加一个用于追踪用户数的变量:
|
||||
|
||||
```js
|
||||
let currentUsers = 0;
|
||||
```
|
||||
|
||||
Now, when someone connects, you should increment the count before emitting the count. So, you will want to add the incrementer within the connection listener.
|
||||
然后,只要有人连接到服务器,就需要在发出用户数量之前先给这个变量加 1。 因此,需要在连接监听器中增加一个递增器。
|
||||
|
||||
```js
|
||||
++currentUsers;
|
||||
```
|
||||
|
||||
Finally, after incrementing the count, you should emit the event (still within the connection listener). The event should be named 'user count', and the data should just be the `currentUsers`.
|
||||
最后,在监听连接的地方发出(emit)该事件即可。 这个事件应命名为 “user count”,且数据应该为 `currentUsers`:
|
||||
|
||||
```js
|
||||
io.emit('user count', currentUsers);
|
||||
```
|
||||
|
||||
Now, you can implement a way for your client to listen for this event! Similar to listening for a connection on the server, you will use the `on` keyword.
|
||||
现在,你实现了在客户端监听此事件。 类似在服务器上监听连接,你将使用 `on` 关键字。
|
||||
|
||||
```js
|
||||
socket.on('user count', function(data) {
|
||||
@@ -36,13 +36,13 @@ socket.on('user count', function(data) {
|
||||
});
|
||||
```
|
||||
|
||||
Now, try loading up your app, authenticate, and you should see in your client console '1' representing the current user count! Try loading more clients up, and authenticating to see the number go up.
|
||||
现在你可以尝试启动你的 app 并登录,你会看到在客户端的控制台打印出了 “1”,这就表示目前连接到服务器的用户数为 1。 你可以试着通过打开多个 app 来验证数量是否会增加。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/28ef7f1078f56eb48c7b1aeea35ba1f5).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/28ef7f1078f56eb48c7b1aeea35ba1f5)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
currentUsers should be defined.
|
||||
应定义 currentUsers。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -60,7 +60,7 @@ currentUsers should be defined.
|
||||
);
|
||||
```
|
||||
|
||||
Server should emit the current user count at each new connection.
|
||||
服务器应在有新的连接时发送当前用户数量。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -78,7 +78,7 @@ Server should emit the current user count at each new connection.
|
||||
);
|
||||
```
|
||||
|
||||
Your client should be listening for 'user count' event.
|
||||
客户端应监听 “user count” 事件。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5895f70df9fc0f352b528e6a
|
||||
title: Create New Middleware
|
||||
title: 创建新的中间件
|
||||
challengeType: 2
|
||||
forumTopicId: 301551
|
||||
dashedName: create-new-middleware
|
||||
@@ -8,9 +8,9 @@ dashedName: create-new-middleware
|
||||
|
||||
# --description--
|
||||
|
||||
As is, any user can just go to `/profile` whether they have authenticated or not, by typing in the url. We want to prevent this, by checking if the user is authenticated first before rendering the profile page. This is the perfect example of when to create a middleware.
|
||||
无论是否登录,任何用户都可以通过输入 url 而跳转到 `/profile`。 为了解决这个问题,我们需要在 profile 页面渲染之前进行用户验证。 这就是一个很棒的创建中间件的示例。
|
||||
|
||||
The challenge here is creating the middleware function `ensureAuthenticated(req, res, next)`, which will check if a user is authenticated by calling passport's `isAuthenticated` method on the `request` which, in turn, checks if `req.user` is defined. If it is, then `next()` should be called, otherwise, we can just respond to the request with a redirect to our homepage to login. An implementation of this middleware is:
|
||||
这个挑战的目标是创建 `ensureAuthenticated(req, res, next)` 中间件方法,通过在 `request` 上调用 passports 的`isAuthenticated` 方法,可以检查 `req.user` 是否定义,从而确定用户是否通过认证。 如果用户已通过验证,就会调用 `next()`,否则我们应重定向到主页并让用户登录。 该中间件的实现如下:
|
||||
|
||||
```js
|
||||
function ensureAuthenticated(req, res, next) {
|
||||
@@ -21,7 +21,7 @@ function ensureAuthenticated(req, res, next) {
|
||||
};
|
||||
```
|
||||
|
||||
Now add *ensureAuthenticated* as a middleware to the request for the profile page before the argument to the get request containing the function that renders the page.
|
||||
然后,在 profile 页面请求中,添加 *ensureAuthenticated* 作为中间件,放在 get 请求(包含渲染页面的函数)的参数之前。
|
||||
|
||||
```js
|
||||
app
|
||||
@@ -31,11 +31,11 @@ app
|
||||
});
|
||||
```
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/ae49b8778cab87e93284a91343da0959).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/ae49b8778cab87e93284a91343da0959)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
Middleware ensureAuthenticated should be implemented and on our /profile route.
|
||||
应把 ensureAuthenticated 中间件添加到 /profile 路由中。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -58,7 +58,7 @@ Middleware ensureAuthenticated should be implemented and on our /profile route.
|
||||
);
|
||||
```
|
||||
|
||||
A Get request to /profile should correctly redirect to / since we are not authenticated.
|
||||
如果没有通过验证,对 /profile 的 GET 请求应重定向到 /。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 589fc831f9fc0f352b528e76
|
||||
title: Handle a Disconnect
|
||||
title: 处理连接断开
|
||||
challengeType: 2
|
||||
forumTopicId: 301552
|
||||
dashedName: handle-a-disconnect
|
||||
@@ -8,9 +8,9 @@ dashedName: handle-a-disconnect
|
||||
|
||||
# --description--
|
||||
|
||||
You may notice that up to now you have only been increasing the user count. Handling a user disconnecting is just as easy as handling the initial connect, except you have to listen for it on each socket instead of on the whole server.
|
||||
你也许注意到,目前为止我们只处理用户数量的增加,没有处理减少。 事实上,处理用户断开连接也很简单。 区别在于,新连接的监听是发生在整个服务器上,但连接断开的监听是发生在每个 socket 上。
|
||||
|
||||
To do this, add another listener inside the existing `'connect'` listener that listens for `'disconnect'` on the socket with no data passed through. You can test this functionality by just logging that a user has disconnected to the console.
|
||||
为此,我们需要在目前的 `'connect'` 监听里面添加另一个监听器,监听 socket 断开连接 `'disconnect'` 的事件。 通过登录已与控制台断开连接的用户,你可以测试这个功能。
|
||||
|
||||
```js
|
||||
socket.on('disconnect', () => {
|
||||
@@ -18,15 +18,15 @@ socket.on('disconnect', () => {
|
||||
});
|
||||
```
|
||||
|
||||
To make sure clients continuously have the updated count of current users, you should decrease the currentUsers by 1 when the disconnect happens then emit the 'user count' event with the updated count!
|
||||
为确保客户端可以看到实时的用户数量,我们应该在用户断开时让 currentUsers 减 1,然后发送 “user count” 事件,并使用修改后的用户数量。
|
||||
|
||||
**Note:** Just like `'disconnect'`, all other events that a socket can emit to the server should be handled within the connecting listener where we have 'socket' defined.
|
||||
**注意:**和 `'disconnect'` 类似,所有 socket 可以发送到服务器的事件,我们都应该在有 “socket” 定义的连接监听器里处理。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/ab1007b76069884fb45b215d3c4496fa).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/ab1007b76069884fb45b215d3c4496fa)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
Server should handle the event disconnect from a socket.
|
||||
服务器应处理断开 socket 连接的事件。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -40,7 +40,7 @@ Server should handle the event disconnect from a socket.
|
||||
);
|
||||
```
|
||||
|
||||
Your client should be listening for 'user count' event.
|
||||
客户端应监听 “user count” 事件。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 58a25c98f9fc0f352b528e7f
|
||||
title: Hashing Your Passwords
|
||||
title: 哈希密码
|
||||
challengeType: 2
|
||||
forumTopicId: 301553
|
||||
dashedName: hashing-your-passwords
|
||||
@@ -8,13 +8,13 @@ dashedName: hashing-your-passwords
|
||||
|
||||
# --description--
|
||||
|
||||
Going back to the information security section, you may remember that storing plaintext passwords is *never* okay. Now it is time to implement BCrypt to solve this issue.
|
||||
回过头来看信息安全,你也许记得在数据库中存储明文密码是*绝对*禁止的。 现在,我们需要引入 BCrypt 来解决这个问题。
|
||||
|
||||
Add BCrypt as a dependency, and require it in your server. You will need to handle hashing in 2 key areas: where you handle registering/saving a new account, and when you check to see that a password is correct on login.
|
||||
添加 BCrypt 作为依赖,并在服务端请求它。 你需要在两个步骤中使用哈希运算:注册和保存新账户,以及登录时检查密码是否正确。
|
||||
|
||||
Currently on our registration route, you insert a user's password into the database like so: `password: req.body.password`. An easy way to implement saving a hash instead is to add the following before your database logic `const hash = bcrypt.hashSync(req.body.password, 12);`, and replacing the `req.body.password` in the database saving with just `password: hash`.
|
||||
目前处理注册的路由中,我们是这样把密码添加到数据库的:`password: req.body.password`。 保存哈希值的一个简单方式是在数据库逻辑中添加 `const hash = bcrypt.hashSync(req.body.password, 12);`,然后把 `req.body.password` 替换为 `password: hash`。
|
||||
|
||||
Finally, on our authentication strategy, we check for the following in our code before completing the process: `if (password !== user.password) { return done(null, false); }`. After making the previous changes, now `user.password` is a hash. Before making a change to the existing code, notice how the statement is checking if the password is **not** equal then return non-authenticated. With this in mind, your code could look as follows to properly check the password entered against the hash:
|
||||
最后,在验证逻辑中,我们已经有这样一段代码执行检查:`if (password !== user.password) { return done(null, false); }`。 现在存储的密码 `user.password` 已经是哈希值了。 在对现有代码进行修改前,注意目前的语句是如何检查如果密码**不**匹配,就返回未认证的。 牢记这一点,你的代码应该是如下,检查输入的密码是否与哈希相对照:
|
||||
|
||||
```js
|
||||
if (!bcrypt.compareSync(password, user.password)) {
|
||||
@@ -22,13 +22,13 @@ if (!bcrypt.compareSync(password, user.password)) {
|
||||
}
|
||||
```
|
||||
|
||||
That is all it takes to implement one of the most important security features when you have to store passwords!
|
||||
当你需要存储密码时,这样做可以有效地提升网站的安全性。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/dc16cca09daea4d4151a9c36a1fab564).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/dc16cca09daea4d4151a9c36a1fab564)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
BCrypt should be a dependency.
|
||||
应存在 BCrypt 依赖。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -47,7 +47,7 @@ BCrypt should be a dependency.
|
||||
);
|
||||
```
|
||||
|
||||
BCrypt should be correctly required and implemented.
|
||||
应正确地引入和调用 BCrypt。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5895f70ef9fc0f352b528e6b
|
||||
title: How to Put a Profile Together
|
||||
title: 如何将 Profile 放在一起
|
||||
challengeType: 2
|
||||
forumTopicId: 301554
|
||||
dashedName: how-to-put-a-profile-together
|
||||
@@ -8,27 +8,27 @@ dashedName: how-to-put-a-profile-together
|
||||
|
||||
# --description--
|
||||
|
||||
Now that we can ensure the user accessing the `/profile` is authenticated, we can use the information contained in `req.user` on our page!
|
||||
现在,我们能确保访问 `/profile` 页面的用户都是经过验证的,这样我们就可以在页面上使用 `req.user` 里的信息了。
|
||||
|
||||
Pass an object containing the property `username` and value of `req.user.username` as the second argument for the render method of the profile view. Then, go to your `profile.pug` view, and add the following line below the existing `h1` element, and at the same level of indentation:
|
||||
传递一个包含属性 `username` 且属性值为 `req.user.username` 的对象,作为 profile 页面的 render 方法的第二个参数。 然后在 `profile.pug`页面,将下面的代码添加到现有的 `h1` 元素下方,处在同一级别的缩进。
|
||||
|
||||
```pug
|
||||
h2.center#welcome Welcome, #{username}!
|
||||
```
|
||||
|
||||
This creates an `h2` element with the class '`center`' and id '`welcome`' containing the text '`Welcome,`' followed by the username.
|
||||
这样就创建了一个 `h2` 元素,具有 '`center`' class,和包含文本 '`Welcome,`' 的 id '`welcome`',以及 username(用户名)。
|
||||
|
||||
Also, in `profile.pug`, add a link referring to the `/logout` route, which will host the logic to unauthenticate a user.
|
||||
另外,在 `profile.pug` 中,添加一个指向 `/logout` 路由的链接,它将托管一个未认证用户的逻辑。
|
||||
|
||||
```pug
|
||||
a(href='/logout') Logout
|
||||
```
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/136b3ad611cc80b41cab6f74bb460f6a).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/136b3ad611cc80b41cab6f74bb460f6a)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
You should correctly add a Pug render variable to /profile.
|
||||
应在 Pug render 中给 /profile 传一个变量。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5895f70df9fc0f352b528e69
|
||||
title: How to Use Passport Strategies
|
||||
title: 如何使用 Passport 策略
|
||||
challengeType: 2
|
||||
forumTopicId: 301555
|
||||
dashedName: how-to-use-passport-strategies
|
||||
@@ -8,21 +8,21 @@ dashedName: how-to-use-passport-strategies
|
||||
|
||||
# --description--
|
||||
|
||||
In the `index.pug` file supplied, there is actually a login form. It has previously been hidden because of the inline JavaScript `if showLogin` with the form indented after it. Before `showLogin` as a variable was never defined, so it never rendered the code block containing the form. Go ahead and on the `res.render` for that page add a new variable to the object `showLogin: true`. When you refresh your page, you should then see the form! This form is set up to **POST** on `/login`, so this is where we should set up to accept the POST and authenticate the user.
|
||||
在提供的 `index.pug` 文件里有一个登录表单。 因为这个表单中存在行内 JavaScript 代码 `if showLogin`,因此它是隐藏的。 因为变量 `showLogin` 未定义,所以表单不会渲染。 在该页面的 `res.render` 里,给 `showLogin: true` 对象添加一个新的变量。 当你刷新页面,就会看到表单! 表单设置为 `/login` 的 **POST**,因此我们在这里接收 POST 请求并验证用户。
|
||||
|
||||
For this challenge you should add the route `/login` to accept a POST request. To authenticate on this route, you need to add a middleware to do so before then sending a response. This is done by just passing another argument with the middleware before your `function(req,res)` with your response! The middleware to use is `passport.authenticate('local')`.
|
||||
在这个挑战中,你需要为 POST 请求添加路由 `/login`。 为了用这个路由进行验证,你需要在发送请求响应之前添加一个中间件。 中间件应作为参数添加到用于处理请求的函数 `function(req,res)` 之前。 对于 passport 的验证中间件,应这样调用:`passport.authenticate('local')`。
|
||||
|
||||
`passport.authenticate` can also take some options as an argument such as: `{ failureRedirect: '/' }` which is incredibly useful, so be sure to add that in as well. The response after using the middleware (which will only be called if the authentication middleware passes) should be to redirect the user to `/profile` and that route should render the view `profile.pug`.
|
||||
`passport.authenticate` 也接收选项作为参数,例如 `{ failureRedirect: '/' }` 就很有用,请记得添加到你的代码中。 如果中间件验证通过,响应应该是将用户重定向到 `/profile`,并渲染 `profile.pug`。
|
||||
|
||||
If the authentication was successful, the user object will be saved in `req.user`.
|
||||
如果验证通过,用户对象将会储存到 `req.user` 中。
|
||||
|
||||
At this point, if you enter a username and password in the form, it should redirect to the home page `/`, and the console of your server should display `'User {USERNAME} attempted to log in.'`, since we currently cannot login a user who isn't registered.
|
||||
这时,由于我们还没有实现注册功能,如果你在表单里输入了用户名和密码,路由将会重定向到主页 `/`,在服务端将会打印 `'User {USERNAME} attempted to log in.'`。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/7ad011ac54612ad53188b500c5e99cb9).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/7ad011ac54612ad53188b500c5e99cb9)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
All steps should be correctly implemented in the server.js.
|
||||
server.js 中应正确执行所有步骤。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -50,7 +50,7 @@ All steps should be correctly implemented in the server.js.
|
||||
);
|
||||
```
|
||||
|
||||
A POST request to /login should correctly redirect to /.
|
||||
到 /login 的 POST 请求应重定向到 /。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5895f70cf9fc0f352b528e67
|
||||
title: Implement the Serialization of a Passport User
|
||||
title: 实现 Passport 用户的序列化
|
||||
challengeType: 2
|
||||
forumTopicId: 301556
|
||||
dashedName: implement-the-serialization-of-a-passport-user
|
||||
@@ -8,11 +8,11 @@ dashedName: implement-the-serialization-of-a-passport-user
|
||||
|
||||
# --description--
|
||||
|
||||
Right now, we're not loading an actual user object since we haven't set up our database. This can be done many different ways, but for our project we will connect to the database once when we start the server and keep a persistent connection for the full life-cycle of the app. To do this, add your database's connection string (for example: `mongodb+srv://:@cluster0-jvwxi.mongodb.net/?retryWrites=true&w=majority`) to the environment variable `MONGO_URI`. This is used in the `connection.js` file.
|
||||
截至目前,我们还没有配置完数据库,因此还无法加载用户数据。 实现这个的方式很多,但对于我们的项目,一旦服务器启动,那么只要有 app 实例在运行,数据库就应一直处于连接状态。 为此,你需要在环境变量 `MONGO_URI` 中添加你的数据库地址(比如:`mongodb+srv://:@cluster0-jvwxi.mongodb.net/?retryWrites=true&w=majority`)。 我们会在 `connection.js` 文件中调用它。
|
||||
|
||||
*You can set up a free database on [MongoDB Atlas](https://www.mongodb.com/cloud/atlas).*
|
||||
*你可以在 [MongoDB Atlas](https://www.mongodb.com/cloud/atlas) 创建一个免费的数据库。*
|
||||
|
||||
Now we want to connect to our database then start listening for requests. The purpose of this is to not allow requests before our database is connected or if there is a database error. To accomplish this, you will want to encompass your serialization and your app routes in the following code:
|
||||
现在我们想要连接到数据库,然后开始监听请求。 这样做的目的是在连接数据库之前或者出现数据库错误时,不接收任何请求。 要实现这一点,你需要在以下代码中包含序列化和应用的路由:
|
||||
|
||||
```js
|
||||
myDB(async client => {
|
||||
@@ -38,13 +38,13 @@ myDB(async client => {
|
||||
// app.listen out here...
|
||||
```
|
||||
|
||||
Be sure to uncomment the `myDataBase` code in `deserializeUser`, and edit your `done(null, null)` to include the `doc`.
|
||||
记得要取消 `deserializeUser` 中 `myDataBase` 的注释,并把 `doc` 添加到 `done(null, null)`。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/175f2f585a2d8034044c7e8857d5add7).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/175f2f585a2d8034044c7e8857d5add7)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
Database connection should be present.
|
||||
应存在数据库连接。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -62,7 +62,7 @@ Database connection should be present.
|
||||
);
|
||||
```
|
||||
|
||||
Deserialization should now be correctly using the DB and `done(null, null)` should be called with the `doc`.
|
||||
序列化应正确使用数据库,应用 `doc` 调用 `done(null, null)`。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 589a69f5f9fc0f352b528e71
|
||||
title: Implementation of Social Authentication II
|
||||
title: 实现第二种社交登录
|
||||
challengeType: 2
|
||||
forumTopicId: 301557
|
||||
dashedName: implementation-of-social-authentication-ii
|
||||
@@ -8,11 +8,11 @@ dashedName: implementation-of-social-authentication-ii
|
||||
|
||||
# --description--
|
||||
|
||||
The last part of setting up your GitHub authentication is to create the strategy itself. For this, you will need to add the dependency of 'passport-github' to your project and require it in your `auth.js` as `GithubStrategy` like this: `const GitHubStrategy = require('passport-github').Strategy;`. Do not forget to require and configure `dotenv` to use your environment variables.
|
||||
设置 GitHub 验证的最后一步是创建策略本身。 为此,你需要在项目中添加 “passport-github” 依赖,并在 `auth.js` 中 请求它,作为 `GithubStrategy`,像这样:`const GitHubStrategy = require('passport-github').Strategy;`。 别忘了请求和配置 `dotenv`,使用你的环境变量。
|
||||
|
||||
To set up the GitHub strategy, you have to tell Passport to use an instantiated `GitHubStrategy`, which accepts 2 arguments: an object (containing `clientID`, `clientSecret`, and `callbackURL`) and a function to be called when a user is successfully authenticated, which will determine if the user is new and what fields to save initially in the user's database object. This is common across many strategies, but some may require more information as outlined in that specific strategy's GitHub README. For example, Google requires a *scope* as well which determines what kind of information your request is asking to be returned and asks the user to approve such access. The current strategy we are implementing has its usage outlined [here](https://github.com/jaredhanson/passport-github/), but we're going through it all right here on freeCodeCamp!
|
||||
为了设置 GitHub 策略,我们需要在 Passport 中使用实例化的 `GitHubStrategy`,它可以接收两个参数:一个对象(包括 `clientID`、`clientSecret` 和 `callbackURL`),以及一个回调函数。在这个回调函数中,我们要处理验证成功时,判断用户是否已经在数据库中存在的逻辑,以及在用户数据库对象中最初保存哪些字段。 这种处理方式适用于绝大部分第三方验证策略,但有些策略会需要我们提供更多的信息,详情请参考相关策略的 GitHub README。 例如,Google 的验证策略会要求你提供一个 *scope*,用于标示用户成功登录后,你需要从返回的对象中获取那些信息。以及,这也需要经过用户同意,你才可以获取到。 你可以在[这里](https://github.com/jaredhanson/passport-github/)了解当前我们使用的验证策略的用法,不过我们也会在 freeCodeCamp 课程中进行详细讲解。
|
||||
|
||||
Here's how your new strategy should look at this point:
|
||||
你的新策略应该这样去实现:
|
||||
|
||||
```js
|
||||
passport.use(new GitHubStrategy({
|
||||
@@ -27,13 +27,13 @@ passport.use(new GitHubStrategy({
|
||||
));
|
||||
```
|
||||
|
||||
Your authentication won't be successful yet, and it will actually throw an error without the database logic and callback, but it should log your GitHub profile to your console if you try it!
|
||||
目前,你的验证部分不会成功。由于没有数据库的逻辑和回调函数,你的代码目前还会报错。但如果你试一试,就可以在控制台里看到输出了你的 GitHub 个人信息。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/ff3a1166684c1b184709ac0bee30dee6).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/ff3a1166684c1b184709ac0bee30dee6)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
passport-github dependency should be added.
|
||||
应正确添加依赖 passport-github。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -52,7 +52,7 @@ passport-github dependency should be added.
|
||||
);
|
||||
```
|
||||
|
||||
passport-github should be required.
|
||||
应正确请求依赖 passport-github。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -70,7 +70,7 @@ passport-github should be required.
|
||||
);
|
||||
```
|
||||
|
||||
GitHub strategy should be setup correctly thus far.
|
||||
到目前为止,Github 策略应正确设置。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 589a8eb3f9fc0f352b528e72
|
||||
title: Implementation of Social Authentication III
|
||||
title: 实现第三种社交登录
|
||||
challengeType: 2
|
||||
forumTopicId: 301558
|
||||
dashedName: implementation-of-social-authentication-iii
|
||||
@@ -8,7 +8,7 @@ dashedName: implementation-of-social-authentication-iii
|
||||
|
||||
# --description--
|
||||
|
||||
The final part of the strategy is handling the profile returned from GitHub. We need to load the user's database object if it exists, or create one if it doesn't, and populate the fields from the profile, then return the user's object. GitHub supplies us a unique *id* within each profile which we can use to search with to serialize the user with (already implemented). Below is an example implementation you can use in your project--it goes within the function that is the second argument for the new strategy, right below where `console.log(profile);` currently is:
|
||||
验证策略的最后一部分是处理从 GitHub 返回的个人信息。 如果用户存在,我们就需要从数据库中读取用户数据并在 profile 页面加载;否则,我们需要把用户信息添加到数据库。 GitHub 在用户信息中为我们提供了独一无二的 *id*,我们可以通过序列化的 id 在数据库中搜索用户(已实现)。 以下是这个逻辑的实现示例,我们应该把它传到新策略的第二个参数,就是目前 `console.log(profile);` 的下方:
|
||||
|
||||
```js
|
||||
myDataBase.findOneAndUpdate(
|
||||
@@ -38,15 +38,15 @@ myDataBase.findOneAndUpdate(
|
||||
);
|
||||
```
|
||||
|
||||
`findOneAndUpdate` allows you to search for an object and update it. If the object doesn't exist, it will be inserted and made available to the callback function. In this example, we always set `last_login`, increment the `login_count` by `1`, and only populate the majority of the fields when a new object (new user) is inserted. Notice the use of default values. Sometimes a profile returned won't have all the information filled out or the user will keep it private. In this case, you handle it to prevent an error.
|
||||
`findOneAndUpdate` 的作用是在数据库中查询对象并更新, 如果对象不存在,将插入对象,然后我们可以在回调方法里获取到插入的新对象。 在这个例子中,我们会设置 `last_login`,而且总会为 `login_count` 加 `1`。只有在插入一个新对象(新用户)时,我们才会初始化这些字段。 另外,还需要注意默认值的使用。 有时返回的用户信息可能不全,可能是因为用户没有填写,也可能是因为用户选择不公开一部分信息。 在这种情况下,我们需要进行相应的处理,以防我们的 app 报错。
|
||||
|
||||
You should be able to login to your app now--try it!
|
||||
你现在应该可以登录你的应用了,试试吧。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/183e968f0e01d81dde015d45ba9d2745).
|
||||
完成上述要求后,你可以在下方提交你的页面链接。 如果你遇到了问题,可以参考 [这里](https://gist.github.com/camperbot/183e968f0e01d81dde015d45ba9d2745) 的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
GitHub strategy setup should be complete.
|
||||
GitHub 策略应配置完成。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 589a69f5f9fc0f352b528e70
|
||||
title: Implementation of Social Authentication
|
||||
title: 实现第一种社交登录
|
||||
challengeType: 2
|
||||
forumTopicId: 301559
|
||||
dashedName: implementation-of-social-authentication
|
||||
@@ -8,21 +8,21 @@ dashedName: implementation-of-social-authentication
|
||||
|
||||
# --description--
|
||||
|
||||
The basic path this kind of authentication will follow in your app is:
|
||||
在应用中这种验证的基本路径是:
|
||||
|
||||
1. User clicks a button or link sending them to our route to authenticate using a specific strategy (e.g. GitHub).
|
||||
2. Your route calls `passport.authenticate('github')` which redirects them to GitHub.
|
||||
3. The page the user lands on, on GitHub, allows them to login if they aren't already. It then asks them to approve access to their profile from our app.
|
||||
4. The user is then returned to our app at a specific callback url with their profile if they are approved.
|
||||
5. They are now authenticated, and your app should check if it is a returning profile, or save it in your database if it is not.
|
||||
1. 在用户点击按钮或者链接后,进入验证页面,通过第三方平台(如 GitHub)来进行用户验证。
|
||||
2. 需要在路由中调用 `passport.authenticate('github')`,跳转至 GitHub 验证页面。
|
||||
3. 页面跳转到 GitHub 上,如果用户未登录 GitHub,就需要在这里进行登录。 登录成功后,会出现向用户申请访问权限的确认页。
|
||||
4. 如果用户同意访问,则用户会回到我们提供的回调地址,带着 GitHub 那边提供的用户信息回到我们的 app 中。
|
||||
5. 验证已完成。我们的应用需要查询这个用户是否已经存在。如果是新用户,那我们需要把用户信息存储到数据库。
|
||||
|
||||
Strategies with OAuth require you to have at least a *Client ID* and a *Client Secret* which is a way for the service to verify who the authentication request is coming from and if it is valid. These are obtained from the site you are trying to implement authentication with, such as GitHub, and are unique to your app--**THEY ARE NOT TO BE SHARED** and should never be uploaded to a public repository or written directly in your code. A common practice is to put them in your `.env` file and reference them like so: `process.env.GITHUB_CLIENT_ID`. For this challenge we're going to use the GitHub strategy.
|
||||
在 OAuth 验证策略中,我们至少需要提供 *Client ID* 和 *Client Secret*,这样第三方平台就会获悉验证请求的来源,以及这个来源是否有效。 为此,需要去我们使用的第三方验证平台(比如 GitHub)获取这两个字段的值。 注意,我们获取到的这个值是唯一的,仅对我们的当前 app 有效——**因此,千万不要分享给别人**,更不要上传到公共仓库或者直接写在代码里。 通常,我们会把它们放在 `.env` 文件里,并通过 `process.env.GITHUB_CLIENT_ID` 获取。 对于这次挑战,我们将会使用 GitHub 作为验证平台。
|
||||
|
||||
Obtaining your *Client ID and Secret* from GitHub is done in your account profile settings under 'developer settings', then '[OAuth applications](https://github.com/settings/developers)'. Click 'Register a new application', name your app, paste in the url to your Repl.it homepage (**Not the project code's url**), and lastly, for the callback url, paste in the same url as the homepage but with `/auth/github/callback` added on. This is where users will be redirected for us to handle after authenticating on GitHub. Save the returned information as `'GITHUB_CLIENT_ID'` and `'GITHUB_CLIENT_SECRET'` in your `.env` file.
|
||||
首先,你需要进入账户设置里的 “developer settings(开发者设置)”板块,在 '[OAuth applications](https://github.com/settings/developers)' 获取 *Client ID and Secret*。 点击 “Register a new application”,设置你的应用名称,然后把你的 Repl.it 主页地址(**不是项目代码的地址**)粘贴到 Homepage URL。然后,回调 url 需要设置成上面 Homepage URL 里你粘贴的地址,但后面要加上 `/auth/github/callback`。 这样在用户通过 Github 验证后才能跳转到我们指定的页面。 在你的 `.env` 文件里将返回的信息保存为 `'GITHUB_CLIENT_ID'` 和 `'GITHUB_CLIENT_SECRET'`。
|
||||
|
||||
In your `routes.js` file, add `showSocialAuth: true` to the homepage route, after `showRegistration: true`. Now, create 2 routes accepting GET requests: `/auth/github` and `/auth/github/callback`. The first should only call passport to authenticate `'github'`. The second should call passport to authenticate `'github'` with a failure redirect to `/`, and then if that is successful redirect to `/profile` (similar to our last project).
|
||||
在你的 `routes.js` 文件中,添加 `showSocialAuth: true` 到主页路由,在 `showRegistration: true` 的后面。 然后,为 `/auth/github` 和 `/auth/github/callback` 创建两个接收 GET 请求的路由。 第一个只需要通过调用 passport 来验证 `'github'`。 第二个应该调用 passport 来验证 `'github'`,但需要在失败时跳转回主页 `/`,成功时跳转到用户页面 `/profile`(跳转的逻辑与上一个项目中的逻辑一样)。
|
||||
|
||||
An example of how `/auth/github/callback` should look is similar to how we handled a normal login:
|
||||
例如 `/auth/github/callback` 应该像我们处理在上一个项目中正常的登录一样:
|
||||
|
||||
```js
|
||||
app.route('/login')
|
||||
@@ -31,11 +31,11 @@ app.route('/login')
|
||||
});
|
||||
```
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project up to this point [here](https://gist.github.com/camperbot/1f7f6f76adb178680246989612bea21e).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/1f7f6f76adb178680246989612bea21e)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
Route /auth/github should be correct.
|
||||
路由 /auth/github 应正确配置。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -53,7 +53,7 @@ Route /auth/github should be correct.
|
||||
);
|
||||
```
|
||||
|
||||
Route /auth/github/callback should be correct.
|
||||
路由 /auth/github/callback 应正确配置。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 58965611f9fc0f352b528e6c
|
||||
title: Logging a User Out
|
||||
title: 用户退出登录
|
||||
challengeType: 2
|
||||
forumTopicId: 301560
|
||||
dashedName: logging-a-user-out
|
||||
@@ -8,9 +8,9 @@ dashedName: logging-a-user-out
|
||||
|
||||
# --description--
|
||||
|
||||
Creating the logout logic is easy. The route should just unauthenticate the user and redirect to the home page instead of rendering any view.
|
||||
创建退出登录的逻辑是比较简单的。 只要用户尝试退出登录,路由就应重定向到主页,而不应该显示任何其他页面。
|
||||
|
||||
In passport, unauthenticating a user is as easy as just calling `req.logout();` before redirecting.
|
||||
在 passport 里,只需要在重定向前调用 `req.logout();` 即可完成用户的退出登录。
|
||||
|
||||
```js
|
||||
app.route('/logout')
|
||||
@@ -20,7 +20,7 @@ app.route('/logout')
|
||||
});
|
||||
```
|
||||
|
||||
You may have noticed that we're not handling missing pages (404). The common way to handle this in Node is with the following middleware. Go ahead and add this in after all your other routes:
|
||||
你可能注意到我们还没有处理 404 错误,这个错误码代表页面无法找到。 在 Node 中我们通常会用如下的中间件来处理。 请在所有路由之后添加这段代码:
|
||||
|
||||
```js
|
||||
app.use((req, res, next) => {
|
||||
@@ -30,11 +30,11 @@ app.use((req, res, next) => {
|
||||
});
|
||||
```
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/c3eeb8a3ebf855e021fd0c044095a23b).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/c3eeb8a3ebf855e021fd0c044095a23b)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
`req.Logout` should be called in your `/logout` route.
|
||||
`req.Logout` 应在 `/logout` 路由中调用。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -52,7 +52,7 @@ Submit your page when you think you've got it right. If you're running into erro
|
||||
);
|
||||
```
|
||||
|
||||
Logout should redirect to the home page.
|
||||
退出登录后应重定向到主页 /。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 58966a17f9fc0f352b528e6d
|
||||
title: Registration of New Users
|
||||
title: 注册新用户
|
||||
challengeType: 2
|
||||
forumTopicId: 301561
|
||||
dashedName: registration-of-new-users
|
||||
@@ -8,11 +8,11 @@ dashedName: registration-of-new-users
|
||||
|
||||
# --description--
|
||||
|
||||
Now we need to allow a new user on our site to register an account. On the `res.render` for the home page add a new variable to the object passed along--`showRegistration: true`. When you refresh your page, you should then see the registration form that was already created in your `index.pug` file! This form is set up to **POST** on `/register`, so this is where we should set up to accept the **POST** and create the user object in the database.
|
||||
现在我们需要为新用户添加注册帐号的功能。 首先我们需要在主页的 `res.render` 接收的变量对象中添加 `showRegistration: true`。 此时刷新页面,你会看到页面上已经显示了我们在 `index.pug` 文件中定义的注册表单。 这个表单设置了请求路径 `/register`,并将请求方法设置成 **POST**,所以我们需要在服务器接受 **POST** 请求,且在数据库中创建用户对象。
|
||||
|
||||
The logic of the registration route should be as follows: Register the new user > Authenticate the new user > Redirect to /profile
|
||||
用户注册的逻辑:注册新用户 > 认证新用户 > 重定向到 /profile。
|
||||
|
||||
The logic of step 1, registering the new user, should be as follows: Query database with a findOne command > if user is returned then it exists and redirect back to home *OR* if user is undefined and no error occurs then 'insertOne' into the database with the username and password, and, as long as no errors occur, call *next* to go to step 2, authenticating the new user, which we've already written the logic for in our POST */login* route.
|
||||
对于步骤一的注册新用户,详细逻辑是这样的:用 findOne 命令查询数据库 > 如果返回了用户对象,则表示用户存在,然后返回主页;*或者*如果用户未定义且没有报错,则会将包含用户名和密码的用户对象通过 “insertOne” 添加到数据库,只要没有报错则会继续*下一步*:认证新用户——我们已经在 */login* 路由的 POST 请求中写好了这部分逻辑。
|
||||
|
||||
```js
|
||||
app.route('/register')
|
||||
@@ -47,13 +47,13 @@ app.route('/register')
|
||||
);
|
||||
```
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/b230a5b3bbc89b1fa0ce32a2aa7b083e).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/b230a5b3bbc89b1fa0ce32a2aa7b083e)的答案。
|
||||
|
||||
**NOTE:** From this point onwards, issues can arise relating to the use of the *picture-in-picture* browser. If you are using an online IDE which offers a preview of the app within the editor, it is recommended to open this preview in a new tab.
|
||||
**注意:**接下来的挑战可能会在运行 *picture-in-picture*(画中画)模式的浏览器中出现问题。 如果你使用的线上 IDE 提供在 IDE 内预览 app 的功能,请考虑打开新的标签页预览。
|
||||
|
||||
# --hints--
|
||||
|
||||
You should register route and display on home.
|
||||
注册路由和显示主页。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -76,7 +76,7 @@ You should register route and display on home.
|
||||
);
|
||||
```
|
||||
|
||||
Registering should work.
|
||||
注册功能应可以正常运行。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
@@ -104,7 +104,7 @@ async (getUserInput) => {
|
||||
};
|
||||
```
|
||||
|
||||
Login should work.
|
||||
登录功能应可以正常运行。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
@@ -153,7 +153,7 @@ async (getUserInput) => {
|
||||
};
|
||||
```
|
||||
|
||||
Logout should work.
|
||||
退出登录功能应可以正常运行。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -171,7 +171,7 @@ Logout should work.
|
||||
);
|
||||
```
|
||||
|
||||
Profile should no longer work after logout.
|
||||
退出登录后,profile 页面应无法访问。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 589fc832f9fc0f352b528e79
|
||||
title: Send and Display Chat Messages
|
||||
title: 发送和显示聊天消息
|
||||
challengeType: 2
|
||||
forumTopicId: 301562
|
||||
dashedName: send-and-display-chat-messages
|
||||
@@ -8,7 +8,7 @@ dashedName: send-and-display-chat-messages
|
||||
|
||||
# --description--
|
||||
|
||||
It's time you start allowing clients to send a chat message to the server to emit to all the clients! In your `client.js` file, you should see there is already a block of code handling when the message form is submitted.
|
||||
是时候开始允许用户向服务器发送聊天消息,以向所有客户端发送消息了! 在 `client.js` 文件里,你应该已经注意到了这段提交消息表单的代码:
|
||||
|
||||
```js
|
||||
$('form').submit(function() {
|
||||
@@ -16,23 +16,23 @@ $('form').submit(function() {
|
||||
});
|
||||
```
|
||||
|
||||
Within the form submit code, you should emit an event after you define `messageToSend` but before you clear the text box `#m`. The event should be named `'chat message'` and the data should just be `messageToSend`.
|
||||
在表单提交代码中,需要处理发送(emit)事件,它应该发生在定义 `messageToSend` 之后,以及清除 `#m` 中的文本之前。 我们称这个事件为 `'chat message'`,需发送的数据为 `messageToSend`。
|
||||
|
||||
```js
|
||||
socket.emit('chat message', messageToSend);
|
||||
```
|
||||
|
||||
Now, on your server, you should be listening to the socket for the event `'chat message'` with the data being named `message`. Once the event is received, it should emit the event `'chat message'` to all sockets `io.emit` with the data being an object containing `name` and `message`.
|
||||
在服务端,我们需要监听包含 `message` 数据的 `'chat message'` 事件。 当事件发生,我们就通过 `io.emit` 把包含 `name` 和 `message` 的 `'chat message'` 事件发送给所有已连接的 socket。
|
||||
|
||||
In `client.js`, you should now listen for event `'chat message'` and, when received, append a list item to `#messages` with the name, a colon, and the message!
|
||||
在 `client.js` 中,我们需要监听 `'chat message'` 事件。只要接收到这个事件,就把包含名字和消息的内容(注意:需要在名字后添加冒号)添加到 `#messages`。
|
||||
|
||||
At this point, the chat should be fully functional and sending messages across all clients!
|
||||
至此,我们已经完成发送信息到所有客户端的功能。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/d7af9864375207e254f73262976d2016).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/d7af9864375207e254f73262976d2016)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
Server should listen for `'chat message'` and then emit it properly.
|
||||
服务端应监听 `'chat message'`,且应在监听到后发送它。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -50,7 +50,7 @@ Server should listen for `'chat message'` and then emit it properly.
|
||||
);
|
||||
```
|
||||
|
||||
Client should properly handle and display the new data from event `'chat message'`.
|
||||
客户端应正确处理和展示从 `'chat message'` 事件发来的新数据。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5895f70cf9fc0f352b528e66
|
||||
title: Serialization of a User Object
|
||||
title: 用户对象的序列化
|
||||
challengeType: 2
|
||||
forumTopicId: 301563
|
||||
dashedName: serialization-of-a-user-object
|
||||
@@ -8,11 +8,11 @@ dashedName: serialization-of-a-user-object
|
||||
|
||||
# --description--
|
||||
|
||||
Serialization and deserialization are important concepts in regards to authentication. To serialize an object means to convert its contents into a small *key* that can then be deserialized into the original object. This is what allows us to know who has communicated with the server without having to send the authentication data, like the username and password, at each request for a new page.
|
||||
序列化和反序列化在身份认证中是很重要的概念。 序列化一个对象就是将其内容转换成一个体积很小的 *key*,后续可以通过它反序列化为原始对象。 这样,服务器就可以在用户未登录时识别用户,或者说给这个用户一个唯一标识,用户也不需要在每次访问不同页面时都给服务器发送用户名和密码。
|
||||
|
||||
To set this up properly, we need to have a serialize function and a deserialize function. In Passport, we create these with `passport.serializeUser( OURFUNCTION )` and `passport.deserializeUser( OURFUNCTION )`
|
||||
我们需要用到序列化和反序列化的方法来进行配置。 passport 为我们提供了 `passport.serializeUser( OURFUNCTION )` 和 `passport.deserializeUser( OURFUNCTION )` 两个方法。
|
||||
|
||||
The `serializeUser` is called with 2 arguments, the full user object and a callback used by passport. A unique key to identify that user should be returned in the callback, the easiest one to use being the user's `_id` in the object. It should be unique as it generated by MongoDB. Similarly, `deserializeUser` is called with that key and a callback function for passport as well, but, this time, we have to take that key and return the full user object to the callback. To make a query search for a Mongo `_id`, you will have to create `const ObjectID = require('mongodb').ObjectID;`, and then to use it you call `new ObjectID(THE_ID)`. Be sure to add MongoDB as a dependency. You can see this in the examples below:
|
||||
`serializeUser` 方法接收两个参数,分别是表示用户的对象和一个回调函数。 其中,回调函数的返回值应为这个用户的唯一标识符:最简单的写法就是让它返回用户的 `_id`。 这个属性是 MongoDB 为用户创建的唯一字段。 类似地,`deserializeUser` 也接收两个参数,分别是在序列化时生成的标识符以及一个回调函数。在回调函数里,我们需要根据根据传入的标识符(比如 \_id)返回表示用户的对象。 为了在 MongoDB 中通过 query(查询语句)获取 `_id` 字段,首先我们需要创建 `const ObjectID = require('mongodb').ObjectID;`;然后调用它:`new ObjectID(THE_ID)`。 当然,这一切的前提都是先引入 MongoDB 作为依赖。 你可以在下面的例子中看到:
|
||||
|
||||
```js
|
||||
passport.serializeUser((user, done) => {
|
||||
@@ -26,13 +26,13 @@ passport.deserializeUser((id, done) => {
|
||||
});
|
||||
```
|
||||
|
||||
NOTE: This `deserializeUser` will throw an error until we set up the DB in the next step, so for now comment out the whole block and just call `done(null, null)` in the function `deserializeUser`.
|
||||
因此,在我们在下一步中配置 DB 前,`deserializeUser` 会抛出错误。所以,现在请先注释掉上面的代码,在 `deserializeUser` 中仅调用 `done(null, null)` 即可。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/7068a0d09e61ec7424572b366751f048).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考 [这里](https://gist.github.com/camperbot/7068a0d09e61ec7424572b366751f048) 的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
You should serialize user function correctly.
|
||||
应该正确地序列化用户函数。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -55,7 +55,7 @@ You should serialize user function correctly.
|
||||
);
|
||||
```
|
||||
|
||||
You should deserialize user function correctly.
|
||||
应该正确地反序列化用户函数。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -78,7 +78,7 @@ You should deserialize user function correctly.
|
||||
);
|
||||
```
|
||||
|
||||
MongoDB should be a dependency.
|
||||
MongoDB 应作为项目的依赖。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -97,7 +97,7 @@ MongoDB should be a dependency.
|
||||
);
|
||||
```
|
||||
|
||||
Mongodb should be properly required including the ObjectId.
|
||||
应该正确请求 Mongodb,包括 ObjectId。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5895f700f9fc0f352b528e63
|
||||
title: Set up a Template Engine
|
||||
title: 设置模板引擎
|
||||
challengeType: 2
|
||||
forumTopicId: 301564
|
||||
dashedName: set-up-a-template-engine
|
||||
@@ -8,25 +8,31 @@ dashedName: set-up-a-template-engine
|
||||
|
||||
# --description--
|
||||
|
||||
As a reminder, this project is built upon the following starter project on [Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-advancednode), or clone from [GitHub](https://github.com/freeCodeCamp/boilerplate-advancednode/).
|
||||
你可以采用下面的任意一种方式完成这些挑战:
|
||||
|
||||
A template engine enables you to use static template files (such as those written in *Pug*) in your app. At runtime, the template engine replaces variables in a template file with actual values which can be supplied by your server. Then it transforms the template into a static HTML file that is sent to the client. This approach makes it easier to design an HTML page and allows for displaying variables on the page without needing to make an API call from the client.
|
||||
- 克隆[这个 GitHub 仓库](https://github.com/freeCodeCamp/boilerplate-advancednode/),在本地完成这些挑战。
|
||||
- 使用[我们在 Repl.it 上的初始化项目](https://repl.it/github/freeCodeCamp/boilerplate-advancednode)来完成这些挑战。
|
||||
- 使用一个你选择的站点生成器来完成项目。 需要确定包含了我们 GitHub 仓库的所有文件。
|
||||
|
||||
Add `pug@~3.0.0` as a dependency in your `package.json` file.
|
||||
完成本项目后,请将一个正常运行的 demo(项目演示)托管在可以公开访问的平台。 然后在 `Solution Link` 框中提交你的项目 URL。
|
||||
|
||||
Express needs to know which template engine you are using. We will use the `set` method to assign `pug` as the `view engine` property's value: `app.set('view engine', 'pug')`
|
||||
你可以在应用的模版引擎中使用静态模板文件(如那些写在 *Pug* 里的)。 在运行时,模版引擎会用服务端的真实数据替换掉模版文件中的变量, 然后将模版转译成发送给客户端的 HTML 静态文件。 这样可以轻松地构造 HTML 页面,允许在页面直接显示变量内容而不需要从客户端发送 API 请求。
|
||||
|
||||
Your page will not load until you correctly render the index file in the `views/pug` directory.
|
||||
在 `package.json` 文件中添加依赖 `pug@~3.0.0`。
|
||||
|
||||
Change the argument of the `res.render()` declaration in the `/` route to be the file path to the `views/pug` directory. The path can be a relative path (relative to views), or an absolute path, and does not require a file extension.
|
||||
Express 需要知道你正在使用哪个模板引擎。 我们将使用 `set` 方法来分配 `pug` 作为 `view engine` 属性的值: `app.set('view engine', 'pug')`。
|
||||
|
||||
If all went as planned, your app home page will stop showing the message "`Pug template is not defined.`" and will now display a message indicating you've successfully rendered the Pug template!
|
||||
在正确渲染 `views/pug` 目录中的索引文件之后,加载页面。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/3515cd676ea4dfceab4e322f59a37791).
|
||||
修改 `/` 路由中 `res.render()` 声明的参数为 `views/pug` 目录的文件路径。 这个路径可以是相对路径(相对于 views),也可以是绝对路径,不需要文件扩展。
|
||||
|
||||
如果一切顺利,那么你的应用主页不会再显示信息 “`Pug template is not defined.`”,而是会显示一个信息表明你已经成功渲染 Pug 模版!
|
||||
|
||||
完成以上要求后,请提交你的页面链接。 如果你遇到问题,可以查看[这里](https://gist.github.com/camperbot/3515cd676ea4dfceab4e322f59a37791)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
Pug should be a dependency.
|
||||
项目中应使用 Pug 作为依赖。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -45,7 +51,7 @@ Pug should be a dependency.
|
||||
);
|
||||
```
|
||||
|
||||
View engine should be Pug.
|
||||
View 引擎应该是 Pug。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -63,7 +69,7 @@ View engine should be Pug.
|
||||
);
|
||||
```
|
||||
|
||||
Use the correct ExpressJS method to render the index page from the response.
|
||||
使用正确的 ExpressJS 方法渲染来自响应的索引页。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -81,7 +87,7 @@ Use the correct ExpressJS method to render the index page from the response.
|
||||
);
|
||||
```
|
||||
|
||||
Pug should be working.
|
||||
Pug 应该正常运行。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5895f70cf9fc0f352b528e65
|
||||
title: Set up Passport
|
||||
title: 设置 Passport
|
||||
challengeType: 2
|
||||
forumTopicId: 301565
|
||||
dashedName: set-up-passport
|
||||
@@ -8,15 +8,15 @@ dashedName: set-up-passport
|
||||
|
||||
# --description--
|
||||
|
||||
It's time to set up *Passport* so we can finally start allowing a user to register or login to an account! In addition to Passport, we will use Express-session to handle sessions. Using this middleware saves the session id as a cookie in the client and allows us to access the session data using that id on the server. This way we keep personal account information out of the cookie used by the client to verify to our server they are authenticated and just keep the *key* to access the data stored on the server.
|
||||
现在我们来创建 *Passport*,最终我们需要用它来实现用户注册和登录。 除了 Passport,我们还会用 Express-session 来处理 session(会话)。 在客户端,我们可以用这个中间件把 session id 储存到 cookie。同时,我们可以在服务器上通过这个 id 访问 session 数据。 通过这种方式,我们无需把用户的个人账号信息存到 cookie,来完成用户的验证。只需要用这个 id 作为 *key* 来访问服务器上用户的数据即可。
|
||||
|
||||
To set up Passport for use in your project, you will need to add it as a dependency first in your package.json. `"passport": "^0.3.2"`
|
||||
为了在你的项目中使用 Passport,首先你需要在 package.json 文件中添加依赖: `"passport": "^0.3.2"`。
|
||||
|
||||
In addition, add Express-session as a dependency now as well. Express-session has a ton of advanced features you can use but for now we're just going to use the basics! `"express-session": "^1.15.0"`
|
||||
此外,还需要添加 Express-session 作为依赖。 Express-session 有许多高级特性,但我们暂时只需要了解其基础功能。 `"express-session": "^1.15.0"`
|
||||
|
||||
You will need to set up the session settings now and initialize Passport. Be sure to first create the variables 'session' and 'passport' to require 'express-session' and 'passport' respectively.
|
||||
现在,我们需要配置 session 并初始化 Passport。 请先创建变量 “session” 和 “passport” 来分别引入 “express-session” 和 “passport”。
|
||||
|
||||
To set up your express app to use the session we'll define just a few basic options. Be sure to add 'SESSION_SECRET' to your .env file and give it a random value. This is used to compute the hash used to encrypt your cookie!
|
||||
为了让 express 应用可以使用 session,我们需要添加一些基础选项。 请在 .env 文件中添加字段 “SESSION_SECRET”,并给它赋一个随机值, 便于加密 cookie、计算哈希。
|
||||
|
||||
```js
|
||||
app.use(session({
|
||||
@@ -27,13 +27,13 @@ app.use(session({
|
||||
}));
|
||||
```
|
||||
|
||||
As well you can go ahead and tell your express app to **use** 'passport.initialize()' and 'passport.session()'. (For example, `app.use(passport.initialize());`)
|
||||
还有,我们需要让 express **使用** “passport.initialize()” 和 “passport.session()”。 (例如,`app.use(passport.initialize());`)。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/4068a7662a2f9f5d5011074397d6788c).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/4068a7662a2f9f5d5011074397d6788c)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
Passport and Express-session should be dependencies.
|
||||
应添加 Passort 和 express-session 作为依赖。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -57,7 +57,7 @@ Passport and Express-session should be dependencies.
|
||||
);
|
||||
```
|
||||
|
||||
Dependencies should be correctly required.
|
||||
依赖应正确引入。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -80,7 +80,7 @@ Dependencies should be correctly required.
|
||||
);
|
||||
```
|
||||
|
||||
Express app should use new dependencies.
|
||||
Express app 可以使用新的依赖。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -103,7 +103,7 @@ Express app should use new dependencies.
|
||||
);
|
||||
```
|
||||
|
||||
Session and session secret should be correctly set up.
|
||||
应正确设置 session 和 session secret。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 589fc830f9fc0f352b528e74
|
||||
title: Set up the Environment
|
||||
title: 设置环境
|
||||
challengeType: 2
|
||||
forumTopicId: 301566
|
||||
dashedName: set-up-the-environment
|
||||
@@ -8,20 +8,20 @@ dashedName: set-up-the-environment
|
||||
|
||||
# --description--
|
||||
|
||||
The following challenges will make use of the `chat.pug` file. So, in your `routes.js` file, add a GET route pointing to `/chat` which makes use of `ensureAuthenticated`, and renders `chat.pug`, with `{ user: req.user }` passed as an argument to the response. Now, alter your existing `/auth/github/callback` route to set the `req.session.user_id = req.user.id`, and redirect to `/chat`.
|
||||
在接下来的挑战中,我们将会用到 `chat.pug` 文件。 首先,在你的 `routes.js` 文件中为 `/chat` 添加一个处理 GET 请求的路由,并给它传入 `ensureAuthenticated`。在回调函数中,我们需要让它渲染 `chat.pug` 文件,并在响应中包含 `{ user: req.user }` 信息。 现在,请修改 `/auth/github/callback` 路由,让它可以像这样设置 user_id:`req.session.user_id = req.user.id`,并在设置完成后重定向至 `/chat`。
|
||||
|
||||
Add `http` and `socket.io` as a dependency and require/instantiate them in your server defined as follows:
|
||||
我们还需要添加 `http` 和 `socket.io` 两个依赖项,并且像这样引入:
|
||||
|
||||
```javascript
|
||||
const http = require('http').createServer(app);
|
||||
const io = require('socket.io')(http);
|
||||
```
|
||||
|
||||
Now that the *http* server is mounted on the *express app*, you need to listen from the *http* server. Change the line with `app.listen` to `http.listen`.
|
||||
现在我们的 *express 应用*已经包含了 *http* 服务,接下来我们需要监听 *http* 服务的事件。 为此,我们需要把 `app.listen` 更新为 `http.listen`。
|
||||
|
||||
The first thing needing to be handled is listening for a new connection from the client. The <dfn>on</dfn> keyword does just that- listen for a specific event. It requires 2 arguments: a string containing the title of the event thats emitted, and a function with which the data is passed though. In the case of our connection listener, we use *socket* to define the data in the second argument. A socket is an individual client who is connected.
|
||||
需要处理的第一件事是监听客户端的新连接。 <dfn>on</dfn> 关键字就是监听这个特定事件。 它接收两个参数:一个是发出的事件的标题字符串,另一个是后续用来传递数据的回调函数。 在连接监听器中,我们用 *socket* 来代表它所包含的数据。 socket 就是指已连接到服务器的客户端。
|
||||
|
||||
To listen for connections to your server, add the following within your database connection:
|
||||
为了可以监听服务器的连接事件,我们在数据库连接的部分加入如下代码:
|
||||
|
||||
```javascript
|
||||
io.on('connection', socket => {
|
||||
@@ -29,24 +29,24 @@ io.on('connection', socket => {
|
||||
});
|
||||
```
|
||||
|
||||
Now for the client to connect, you just need to add the following to your `client.js` which is loaded by the page after you've authenticated:
|
||||
对于发出连接事件的客户端,只需要在认证后页面加载出的 `client.js` 中添加以下内容:
|
||||
|
||||
```js
|
||||
/*global io*/
|
||||
let socket = io();
|
||||
```
|
||||
|
||||
The comment suppresses the error you would normally see since 'io' is not defined in the file. We've already added a reliable CDN to the Socket.IO library on the page in chat.pug.
|
||||
在这个文件中,我们没有定义 “io” 变量,但第一行的注释会阻止运行时产生的报错。 不过,我们在 chat.pug 的页面上已经为你添加好了 Socket.IO 库的 CDN。
|
||||
|
||||
Now try loading up your app and authenticate and you should see in your server console 'A user has connected'!
|
||||
现在你可以重启一下你的 app,尝试一下验证用户,然后你应该会看到服务器的 console 里输出了 “A user has connected”。
|
||||
|
||||
**Note:**`io()` works only when connecting to a socket hosted on the same url/server. For connecting to an external socket hosted elsewhere, you would use `io.connect('URL');`.
|
||||
**注意:**只有在连接到处于同一个 url/server 上的 socket 时,`io()`才可以正常执行。 如果需要连接到外部的 socket,就需要这样调用:`io.connect('URL');`。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/aae41cf59debc1a4755c9a00ee3859d1).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考[这里](https://gist.github.com/camperbot/aae41cf59debc1a4755c9a00ee3859d1)的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
`socket.io` should be a dependency.
|
||||
应添加 `socket.io` 作为依赖。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -65,7 +65,7 @@ Submit your page when you think you've got it right. If you're running into erro
|
||||
);
|
||||
```
|
||||
|
||||
You should correctly require and instantiate `http` as `http`.
|
||||
应正确引入 `http`,并实例化为 `http`。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -83,7 +83,7 @@ You should correctly require and instantiate `http` as `http`.
|
||||
);
|
||||
```
|
||||
|
||||
You should correctly require and instantiate `socket.io` as `io`.
|
||||
应正确引入 `socket.io`,并实例化为 `io`。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -101,7 +101,7 @@ You should correctly require and instantiate `socket.io` as `io`.
|
||||
);
|
||||
```
|
||||
|
||||
Socket.IO should be listening for connections.
|
||||
Socket.IO 应监听连接。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
@@ -119,7 +119,7 @@ Socket.IO should be listening for connections.
|
||||
);
|
||||
```
|
||||
|
||||
Your client should connect to your server.
|
||||
客户端应连接到服务器。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5895f70bf9fc0f352b528e64
|
||||
title: Use a Template Engine's Powers
|
||||
title: 使用模板引擎
|
||||
challengeType: 2
|
||||
forumTopicId: 301567
|
||||
dashedName: use-a-template-engines-powers
|
||||
@@ -8,23 +8,23 @@ dashedName: use-a-template-engines-powers
|
||||
|
||||
# --description--
|
||||
|
||||
One of the greatest features of using a template engine is being able to pass variables from the server to the template file before rendering it to HTML.
|
||||
模版引擎最大的特点之一就是在 HTML 页面展示之前,可以从服务端传变量到模版文件。
|
||||
|
||||
In your Pug file, you're able to use a variable by referencing the variable name as `#{variable_name}` inline with other text on an element or by using an equal sign on the element without a space such as `p=variable_name` which assigns the variable's value to the p element's text.
|
||||
在 Pug 文件中,你可以用变量名来调用变量,比如写成 `#{variable_name}` 来实现行内调用,或像 `p=variable_name` 把元素与变量直接写在一起,这表示 p 元素的内容等价于这个变量。
|
||||
|
||||
We strongly recommend looking at the syntax and structure of Pug [here](https://github.com/pugjs/pug) on GitHub's README. Pug is all about using whitespace and tabs to show nested elements and cutting down on the amount of code needed to make a beautiful site.
|
||||
建议大家在 [Pug 的 README](https://github.com/pugjs/pug) 里看看它的语法和用法,这样你写出的代码会相对简练。 另外要注意,Pug 使用缩进来表示嵌套的代码块。
|
||||
|
||||
Looking at our pug file 'index.pug' included in your project, we used the variables *title* and *message*.
|
||||
在 pug 的 'index.pug' 文件中,我们使用了 *title* 和 *message* 两个变量。
|
||||
|
||||
To pass those along from our server, you will need to add an object as a second argument to your *res.render* with the variables and their values. For example, pass this object along setting the variables for your index view: `{title: 'Hello', message: 'Please login'}`
|
||||
为了从服务器传递这些信息,你需要给 *res.render* 的第二个参数传入一个对象,其中包含变量对应的值。 比如,如果你想传递对象 `{title: 'Hello', message: 'Please login'}` 到你的主页,
|
||||
|
||||
It should look like: `res.render(process.cwd() + '/views/pug/index', {title: 'Hello', message: 'Please login'});` Now refresh your page and you should see those values rendered in your view in the correct spot as laid out in your index.pug file!
|
||||
看起来应该像这样:`res.render(process.cwd() + '/views/pug/index', {title: 'Hello', message: 'Please login'});`。现在刷新页面,你应该看到那些值就像在 index.pug 文件中一样被渲染在页面上正确的位置。
|
||||
|
||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point [here](https://gist.github.com/camperbot/4af125119ed36e6e6a8bb920db0c0871).
|
||||
完成上述要求后,请提交你的页面链接。 如果你遇到了问题,可以参考 [这里](https://gist.github.com/camperbot/4af125119ed36e6e6a8bb920db0c0871) 的答案。
|
||||
|
||||
# --hints--
|
||||
|
||||
Pug should correctly render variables.
|
||||
Pug 应正确地展示变量。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
|
Reference in New Issue
Block a user