-
Notifications
You must be signed in to change notification settings - Fork 0
/
serverHelper.js
112 lines (108 loc) · 3.57 KB
/
serverHelper.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import { Meteor } from "meteor/meteor";
import { WebApp } from "meteor/webapp";
import { Accounts } from "meteor/accounts-base";
import { check } from "meteor/check";
import cookieParser from "cookie-parser";
// This method will be patch Meteor.user() to return
// the user with login token inferred using the cookie
// in the request. The patch only happens on http request
// and when invoked outside methods or publications.
WebApp.connectHandlers.use(cookieParser()).use(function(req, res, next) {
const currentInvocation =
DDP._CurrentMethodInvocation.get() ||
DDP._CurrentPublicationInvocation.get();
// If it is the server and we're outside the a method or publication
// then patch the Account.user() method to use the user from cookie.
if (Meteor.isServer && !currentInvocation) {
const promise = new Promise(async resolve => {
// Use the cookie if found to fetch the user
const ssrHelper = new SSRServerHelper({ request: req });
const user = await ssrHelper.getUser();
resolve(user);
});
promise.then(user => {
// Patch meteor user method to return the
// SSRed user.
Meteor.user = () => {
// Add a flag to indicate that this is an
// SSRed user as oppose to a DDP connection user.
if (user) {
user.ssrUser = true;
}
return user;
};
next();
});
}
});
export class SSRServerHelper {
constructor(sink) {
this.dataMap = new Map();
this.sink = sink;
}
/**
* Set a {key,value} pair to be fetched by the client
* @param key
* @param value
*/
setItem = (key, value) => {
this.dataMap.set(key, value);
};
/**
*
* @param injectUser - setItem('user', userDoc) to be fetched by client
* @returns {Promise<null>}
*/
getUser = async () => {
if (!this.sink) {
console.error("sink object is null");
} else {
const loginToken = ((this.sink.request || {}).cookies || {})
.meteor_login_token;
if (loginToken) {
check(loginToken, String);
const hashedToken = Accounts._hashLoginToken(loginToken);
const user = await Meteor.users.rawCollection().findOne({
"services.resume.loginTokens.hashedToken": hashedToken
});
if (user) {
// find the right login token corresponding, the current user may have
// several sessions logged on different browsers / computers
const tokenInformation = user.services.resume.loginTokens.find(
tokenInfo => tokenInfo.hashedToken === hashedToken
);
const expiresAt = Accounts._tokenExpiration(tokenInformation.when);
const isExpired = expiresAt < new Date();
if (!isExpired) return user;
}
}
return null;
}
};
/**
* Inject the data in the body of the server rendered page,
* @param injectUser - inject SSRed user, default true.
*/
injectData = (injectUser = true) => {
const dataMap = this.dataMap;
// Inject the user object
if (injectUser && !dataMap["user"]) {
const user = Meteor.user();
// Remove user services for security and performance
if (user) {
delete user.services;
}
dataMap.set("user", user);
}
const SSRObj = {};
dataMap.forEach((value, key) => {
SSRObj[key] = value;
});
console.info("SSRServerHelper - injecting SSR data:");
console.info(SSRObj);
const encodedData = encodeURIComponent(JSON.stringify(SSRObj));
this.sink.appendToBody(
`<script type="text/injected-data" id='injected-data'}>${encodedData}</script>`
);
};
}