/
renderTemplate.js
140 lines (128 loc) · 5.73 KB
/
renderTemplate.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/**
* Copyright 2017 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
// needed to fix "Error: The XMLHttpRequest compatibility library was not found." in Firebase client SDK.
global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
const functions = require('firebase-functions');
const path = require('path');
const fs = require('fs');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const _ = require('lodash');
const baseTemplate = fs.readFileSync(path.resolve(__dirname, './index.html'));
const template = _.template(baseTemplate);
const app = require('../frontend/App');
const express = require('express');
const router = new express.Router();
const firebaseMiddleware = require('./firebase-express-middleware');
const createMemoryHistory = require('history').createMemoryHistory;
const firebase = require('firebase');
// Get the Firebase config from the auto generated file.
const firebaseConfig = require('../frontend/firebase-config.json').result;
// Create a Firebase Admin app
const admin = require('firebase-admin');
const serviceAccount = require('./service-account-credentials.json');
const firebaseAdminApp = admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
}, '__service_account');
const cacheControlHeaderValues = {};
// This Express middleware will check ig there is a Firebase ID token and inject
router.use(firebaseMiddleware.auth({
checkCookie: true,
generateCustomToken: true,
firebaseAdminApp: firebaseAdminApp
}));
router.get('*', (req, res) => {
const user = req.user || {};
createFirebaseAppWithSignedInUser(user.uid, user.token).then(firebaseApp => {
// We make sure that the firebase auth state listeners are triggered again.
// Create the redux store.
const history = createMemoryHistory();
// Set the new URL.
history.replace(req.url);
const store = app.makeStore(history, firebaseApp);
const registry = app.makeRegistry();
// Wait for auth to be ready.
console.log('Waiting for Auth state to be ready in Redux store');
store.firebaseAuthIsReady.then(() => {
console.log('Auth state ready in Redux store');
// Render the App.
const body = ReactDOMServer.renderToString(
React.createElement(app.App, {registry: registry, store: store, history: history})
);
// Get the state of the redux store.
const initialState = store.getState();
// Grab the CSS from our sheetsRegistry.
const css = registry.toString();
// Check if there has been a redirect.
const lastUrl = initialState.router.location.pathname;
if (lastUrl !== req.url) {
// If there has been a redirect we redirect server side.
console.log('Server side redirect to', lastUrl);
res.redirect(lastUrl);
} else {
// res.set('Cache-Control', 'public, max-age=60, s-maxage=180'); // TODO: make this change dependent on each URL. with a map maybe??
// If there was no redirect we send the rendered app as well as the redux state.
res.send(template({body, initialState, css, node_env: process.env.NODE_ENV}));
}
});
}).catch(error => {
console.log('There was an error', error);
res.status(500).send(error);
});
});
/**
* Helper function to get the markup from React, inject the initial state, and
* send the server-side markup to the client
*/
exports = module.exports = functions.https.onRequest(router);
/**
* Returns a Firebase App instance
*
* @param {String} uid - The UID of the user to sign in the app.
* @param {String} customToken - A custom token to sign the user in the app.
* @return {Promise<Object>} - A Firebase App instance specific to the given user with the user already signed-in.
*/
function createFirebaseAppWithSignedInUser(uid = undefined, customToken) {
// Instantiate a Firebase app.
let firebaseApp;
// Try to re-use cached firebase App.
try {
firebaseApp = firebase.app(/* uid */); // Uncomment. aka create named apps whe this bug is fixed: https://github.com/prescottprue/react-redux-firebase/issues/250
console.log('Re-used a cached app for UID', uid);
} catch(e) {
firebaseApp = firebase.initializeApp(firebaseConfig/* , uid */); // Uncomment. aka create named apps when this bug is fixed: https://github.com/prescottprue/react-redux-firebase/issues/250
console.log('Created a new Firebase App instance for UID', uid);
}
// Check if a Firebase user was signed in and a custom auth token was generated.
let signInPromise;
const firebaseAppUid = firebaseApp.auth().currentUser ? firebaseApp.auth().currentUser.uid : undefined;
if (uid === firebaseAppUid) {
signInPromise = Promise.resolve();
console.log('Firebase App instance auth state is already correct.');
} else if (uid && customToken) {
console.log('Need to sign in user in Firebase App instance.');
signInPromise = firebaseApp.auth().signInWithCustomToken(customToken).then(user => {
console.log('User now signed-in! uid:', user.uid);
});
} else {
console.log('Need to sign out user in Firebase App instance.');
signInPromise = firebaseApp.auth().signOut().then(() => {
console.log('User now signed-out!');
});
}
return signInPromise.then(() => firebaseApp);
}