Skip to content

Commit 8c68498

Browse files
authored
Merge pull request #84 from jysperm/koa
添加对 koa 的支持
2 parents c4bd63d + 7941309 commit 8c68498

21 files changed

Lines changed: 493 additions & 286 deletions

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ language: node_js
22
node_js:
33
- "0.12"
44
- "4.2"
5+
6+
script:
7+
- npm test
8+
- npm run test-koa

API.md

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ AV.express(options?: object)
2525

2626
初始化一个 LeanEngine 中间件,可被挂载到 express 应用上。
2727

28+
## AV.koa
29+
30+
```javascript
31+
AV.koa(options?: object)
32+
```
33+
34+
初始化一个 LeanEngine 中间件,可被挂载到 koa 应用上。
35+
2836
## AV.Object
2937

3038
* `AV.Object#disableBeforeHook()`
@@ -196,26 +204,33 @@ AV.Cloud.httpRequest({
196204
197205
### cookie-session
198206
199-
该中间件提供了在 Express 中维护用户状态的能力
207+
该中间件提供了在 Express 或 Koa 中维护用户状态的能力,在 Express 中:
200208
201209
```javascript
202-
app.use(AV.Cloud.CookieSession({ secret: 'my secret', maxAge: 3600000, fetchUser: true }));
210+
app.use(AV.Cloud.CookieSession({secret: 'my secret', maxAge: 3600000, fetchUser: true}));
211+
```
212+
213+
在 Koa 中(添加 `framework: 'koa'` 参数):
214+
215+
```javascript
216+
app.use(AV.Cloud.CookieSession({framework: 'koa', secret: 'my secret', maxAge: 3600000, fetchUser: true}));
203217
```
204218
205219
参数包括:
206220
221+
* `koa?: boolean`:返回一个 koa(而不是 express)中间件。
207222
* `secret: string`:对 Cookie 进行签名的密钥,请选用一个随机字符串。
208223
* `name?: string`:Cookie 名称,默认为 `avos.sess`
209224
* `maxAge?: number`:Cookie 过期时间。
210225
* `fetchUser?: boolean`:是否自动查询用户信息,默认为 `false`,即不自动查询,这种情况下只能访问用户的 `id``sessionToken`.
211226
* `httpOnly?: boolean`: 不允许客户端读写该 Cookie,默认 `false`.
212227
213-
express 的 `Request` 上会有这些属性可用:
228+
express 的 `Request`(或 koa 的 `ctx.request`上会有这些属性可用:
214229
215230
* `currentUser?: AV.User`:和当前客户端关联的用户信息(根据 Cookie),如未开启 `cookie-session``fetchUser` 选项则只可以访问 `id``sessionToken`.
216231
* `sessionToken?: string`:和当前客户端关联的 `sessionToken`(根据 Cookie)。
217232
218-
express 的 `Response` 上会有这些属性可用:
233+
express 的 `Response`(或 koa 的 `ctx.response`上会有这些属性可用:
219234
220235
* `saveCurrentUser(user: AV.User)`:将当前客户端与特定用户关联(会写入 Cookie)。
221236
* `clearCurrentUser()`:清除当前客户端关联的用户(删除 Cookie)。
@@ -224,8 +239,16 @@ express 的 `Response` 上会有这些属性可用:
224239
225240
### https-redirect
226241
227-
该中间件会自动将 HTTP 请求重定向到 HTTPS 上:
242+
该中间件会自动将 HTTP 请求重定向到 HTTPS 上,在 Express 中
228243
229244
```javascript
245+
app.enable('trust proxy');
230246
app.use(AV.Cloud.HttpsRedirect());
231247
```
248+
249+
Koa 中(添加 `framework: 'koa'` 参数):
250+
251+
```javascript
252+
app.proxy = true;
253+
app.use(AV.Cloud.HttpsRedirect({framework: 'koa'}));
254+
```

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ npm install leanengine@next --save
2222
* 网站托管开发指南:<https://leancloud.cn/docs/leanengine_webhosting_guide-node.html>
2323
* 云函数开发指南:<https://leancloud.cn/docs/leanengine_cloudfunction_guide-node.html>
2424
* 云引擎命令行工具使用详解:<https://leancloud.cn/docs/leanengine_cli.html>
25+
* API 参考文档:<https://github.com/leancloud/leanengine-node-sdk/blob/master/API.md>
2526

2627
## 项目示例
2728

lib/leanengine.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ AV.express = function(options) {
3030
return router;
3131
};
3232

33+
AV.koa = function(options) {
34+
return require('../middleware/koa')(AV);
35+
};
36+
3337
// override AV.Cloud to a connect app
3438
if (!AV._old_Cloud) {
3539
AV._old_Cloud = AV.Cloud;
@@ -56,8 +60,21 @@ if (https.globalAgent && https.globalAgent.options) {
5660
https.globalAgent.options.rejectUnauthorized = false;
5761
}
5862

59-
AV.Cloud.CookieSession = require('../middleware/cookie-session')(AV);
60-
AV.Cloud.HttpsRedirect = require('../middleware/https-redirect')(AV);
63+
AV.Cloud.CookieSession = function(options) {
64+
if (options && options.framework == 'koa') {
65+
return require('../middleware/cookie-session-koa')(AV)(options);
66+
} else {
67+
return require('../middleware/cookie-session')(AV)(options);
68+
}
69+
};
70+
71+
AV.Cloud.HttpsRedirect = function(options) {
72+
if (options && options.framework == 'koa') {
73+
return require('../middleware/https-redirect-koa')(AV)(options);
74+
} else {
75+
return require('../middleware/https-redirect')(AV)(options);
76+
}
77+
}
6178

6279
function createCloudFunctionRouter(options) {
6380
options = options || {};

lib/utils.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,7 @@ exports.prepareResponseObject = function(res, callback) {
7474
var getRemoveAddress = exports.getRemoveAddress = function(req) {
7575
return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress
7676
};
77+
78+
exports.endsWith = function(str, suffix) {
79+
return str.indexOf(suffix, str.length - suffix.length) !== -1;
80+
};

middleware/cookie-session-koa.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module.exports = function(AV) {
2+
return function (options) {
3+
var middleware = require('./cookie-session')(AV)(options);
4+
5+
return function *(next) {
6+
yield middleware.bind(null, this.req, this.res);
7+
8+
this.request.currentUser = this.req.currentUser;
9+
this.request.sessionToken = this.req.sessionToken;
10+
11+
this.response.saveCurrentUser = this.res.saveCurrentUser;
12+
this.response.clearCurrentUser = this.res.clearCurrentUser;
13+
14+
yield next;
15+
}
16+
};
17+
};

middleware/cookie-session.js

Lines changed: 103 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,120 @@
1-
/**
2-
* update from cookie-sesion middleware
3-
*/
4-
(function() {
5-
'use strict';
6-
var Cookies = require('cookies');
7-
var onHeaders = require('on-headers');
8-
var debug = require('debug')('AV:cookieSession');
9-
10-
module.exports = function(AV) {
11-
return function(opts) {
12-
opts = opts || {};
13-
14-
// name - previously "opts.key"
15-
var name = opts.name || opts.key || 'avos:sess';
16-
17-
// secrets
18-
var keys = opts.keys;
19-
if (!keys && opts.secret) {
20-
keys = [opts.secret];
21-
}
1+
'use strict';
222

23-
// defaults
24-
if (!opts.overwrite) {
25-
opts.overwrite = true;
26-
}
27-
opts.httpOnly = true;
28-
opts.signed = true;
3+
var Cookies = require('cookies');
4+
var onHeaders = require('on-headers');
5+
var debug = require('debug')('AV:cookieSession');
296

30-
if (!keys && opts.signed) {
31-
throw new Error('.keys required for avos cookie sessions.');
32-
}
7+
module.exports = function(AV) {
8+
return function(opts) {
9+
opts = opts || {};
3310

34-
debug('session options %j', opts);
11+
// name - previously "opts.key"
12+
var name = opts.name || opts.key || 'avos:sess';
3513

36-
return function cookieSession(req, res, next) {
37-
var cookies = req.sessionCookies = new Cookies(req, res, keys);
38-
var responseUser;
14+
// secrets
15+
var keys = opts.keys;
16+
if (!keys && opts.secret) {
17+
keys = [opts.secret];
18+
}
3919

40-
// 兼容 connect
41-
if (!res.req) res.req = req;
42-
if (!req.res) req.res = res;
20+
// defaults
21+
if (!opts.overwrite) {
22+
opts.overwrite = true;
23+
}
24+
opts.httpOnly = true;
25+
opts.signed = true;
4326

44-
// to pass to Session()
45-
req.sessionOptions = opts;
46-
req.sessionKey = name;
27+
if (!keys && opts.signed) {
28+
throw new Error('.keys required for avos cookie sessions.');
29+
}
4730

48-
res.saveCurrentUser = function(user) {
49-
responseUser = user;
50-
};
31+
debug('session options %j', opts);
5132

52-
res.clearCurrentUser = function() {
53-
responseUser = null;
54-
};
33+
return function cookieSession(req, res, next) {
34+
var cookies = req.sessionCookies = new Cookies(req, res, keys);
35+
var responseUser;
5536

56-
onHeaders(res, function setHeaders() {
57-
var session = null;
37+
// 兼容 connect
38+
if (!res.req) res.req = req;
39+
if (!req.res) req.res = res;
5840

59-
if (responseUser) {
60-
session = {
61-
_uid: responseUser.id,
62-
_sessionToken: responseUser._sessionToken
63-
};
41+
// to pass to Session()
42+
req.sessionOptions = opts;
43+
req.sessionKey = name;
6444

65-
debug('session %j', session);
66-
cookies.set(name, encode(session), opts);
67-
} else if (responseUser === null) {
68-
debug('clear session');
69-
cookies.set(name, '', opts);
70-
}
71-
});
45+
res.saveCurrentUser = function(user) {
46+
responseUser = user;
47+
};
48+
49+
res.clearCurrentUser = function() {
50+
responseUser = null;
51+
};
7252

73-
var session = {};
74-
var json = cookies.get(name, opts);
75-
if (json) {
76-
session = decode(json);
53+
onHeaders(res, function setHeaders() {
54+
var session = null;
55+
56+
if (responseUser) {
57+
session = {
58+
_uid: responseUser.id,
59+
_sessionToken: responseUser._sessionToken
60+
};
61+
62+
debug('session %j', session);
63+
cookies.set(name, encode(session), opts);
64+
} else if (responseUser === null) {
65+
debug('clear session');
66+
cookies.set(name, '', opts);
7767
}
78-
var uid = session._uid;
79-
var sessionToken = session._sessionToken;
80-
req.AV = req.AV || {};
81-
if (uid && sessionToken) {
82-
AV.Cloud.logInByIdAndSessionToken(uid, sessionToken, opts.fetchUser, function(err, user) {
83-
if(err) {
84-
debug('sessionToken invalid, uid: %s', uid);
85-
} else {
86-
req.AV.user = user;
87-
req.currentUser = user;
88-
req.sessionToken = user.getSessionToken();
89-
}
90-
return next();
91-
});
92-
} else {
68+
});
69+
70+
var session = {};
71+
var json = cookies.get(name, opts);
72+
if (json) {
73+
session = decode(json);
74+
}
75+
var uid = session._uid;
76+
var sessionToken = session._sessionToken;
77+
req.AV = req.AV || {};
78+
if (uid && sessionToken) {
79+
AV.Cloud.logInByIdAndSessionToken(uid, sessionToken, opts.fetchUser, function(err, user) {
80+
if(err) {
81+
debug('sessionToken invalid, uid: %s', uid);
82+
} else {
83+
req.AV.user = user;
84+
req.currentUser = user;
85+
req.sessionToken = user.getSessionToken();
86+
}
9387
return next();
94-
}
95-
};
88+
});
89+
} else {
90+
return next();
91+
}
9692
};
9793
};
94+
};
95+
96+
/**
97+
* Decode the base64 cookie value to an object.
98+
*
99+
* @param {String} string
100+
* @return {Object}
101+
* @private
102+
*/
103+
104+
function decode(string) {
105+
var body = new Buffer(string, 'base64').toString('utf8');
106+
return JSON.parse(body);
107+
}
108+
109+
/**
110+
* Encode an object into a base64-encoded JSON string.
111+
*
112+
* @param {Object} body
113+
* @return {String}
114+
* @private
115+
*/
98116

99-
/**
100-
* Decode the base64 cookie value to an object.
101-
*
102-
* @param {String} string
103-
* @return {Object}
104-
* @private
105-
*/
106-
107-
function decode(string) {
108-
var body = new Buffer(string, 'base64').toString('utf8');
109-
return JSON.parse(body);
110-
}
111-
112-
/**
113-
* Encode an object into a base64-encoded JSON string.
114-
*
115-
* @param {Object} body
116-
* @return {String}
117-
* @private
118-
*/
119-
120-
function encode(body) {
121-
body = JSON.stringify(body);
122-
return new Buffer(body).toString('base64');
123-
}
124-
}).call(this);
117+
function encode(body) {
118+
body = JSON.stringify(body);
119+
return new Buffer(body).toString('base64');
120+
}

middleware/cors.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module.exports = function() {
33
res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*');
44

55
if (req.method.toLowerCase() === 'options') {
6+
res.statusCode = 200;
67
res.setHeader('Access-Control-Max-Age','86400');
78
res.setHeader('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
89
res.setHeader('Access-Control-Allow-Headers', 'X-LC-Id, X-LC-Key, X-LC-Session, X-LC-Sign, X-LC-Prod, X-LC-UA, X-Uluru-Application-Key, X-Uluru-Application-Id, X-Uluru-Application-Production, X-Uluru-Client-Version, X-Uluru-Session-Token, X-AVOSCloud-Application-Key, X-AVOSCloud-Application-Id, X-AVOSCloud-Application-Production, X-AVOSCloud-Client-Version, X-AVOSCloud-Session-Token, X-AVOSCloud-Super-Key, X-Requested-With, Content-Type, X-AVOSCloud-Request-sign');

middleware/health-check.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ var packageVersion = require('../package').version;
33
module.exports = function() {
44
return function(req, res, next) {
55
if (req.url == '/__engine/1/ping') {
6+
res.statusCode = 200;
67
res.setHeader('content-type', 'application/json');
78
res.end(JSON.stringify({
89
runtime: 'nodejs-' + process.version,

0 commit comments

Comments
 (0)