-
Notifications
You must be signed in to change notification settings - Fork 147
Expand file tree
/
Copy pathprerender.ts
More file actions
90 lines (72 loc) · 3.21 KB
/
prerender.ts
File metadata and controls
90 lines (72 loc) · 3.21 KB
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
/* eslint-disable */
import express from 'express';
import { getFirestore } from 'firebase-admin/firestore';
import * as functions from 'firebase-functions';
import fetch from 'node-fetch';
const app = express();
const getRendertronServer = async () => {
const doc = await getFirestore().collection('config').doc('rendertron').get();
return doc.data().server;
};
const normalizeHost = (h: string) =>
h.replace(/^https?:\/\//, '').replace(/\/+$/, '');
const resolveHost = (req): string => {
// 1. Prefer Hosting/CDN header
const headerHost =
req.get('x-forwarded-host') || req.get('host') || req.hostname;
if (headerHost) {
return normalizeHost(headerHost);
}
// 2. Fallback to Firebase config
const cfgHost = functions.config()?.site?.domain as string | undefined;
if (cfgHost) {
return normalizeHost(cfgHost);
}
// 3. Last fallback (safe default for dev)
return 'localhost:5000';
};
/**
* checkForBots() - regex that UserAgent, find me a linkbot
* @param {String} userAgent
*/
const checkForBots = (userAgent) => {
// These are link bots only!
// DO NOT ADD GOOGLEBOT.
// If you add Googlebot to this, you will not have a good day.
// This is a mix of Sam Li's list (https://github.com/webcomponents/webcomponents.org/blob/696eb6d6f1fe955db395e96d97c3d1dfe0a02b26/client/bot-filter.py#L9)
// and my list (https://github.com/justinribeiro/blog-pwa/blob/a7174657f3e910cacf2f089c012d40bec719293e/appengine/main.py#L28)
const botList =
'baiduspider|facebookexternalhit|twitterbot|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator|slackbot|facebot|developers.google.com/+/web/snippet/'.toLowerCase();
// FIND THE BOT AMONG THE USSSERRRS
return userAgent.toLowerCase().search(botList) !== -1;
};
// This WILL NOT run for index.html because Exact-match static content is before
// configured rewrites (see "Hosting Priorities" https://firebase.google.com/docs/hosting/url-redirects-rewrites)
//
// The trick is on L66, pwaShell(): You must update that file! Open for explainer.
app.get('*', async (req, res) => {
const host = resolveHost(req);
const botResult = checkForBots(req.headers['user-agent']);
if (botResult) {
// Bot path via Rendertron (you can keep your caching here if you want)
const targetUrl = `https://${host}${req.originalUrl}`;
const rendertron = await getRendertronServer();
const botResp = await fetch(`${rendertron}/render/${targetUrl}`);
const body = await botResp.text();
res.set('Cache-Control', 'public, max-age=300, s-maxage=600');
res.set('Vary', 'User-Agent');
return res.send(body.toString());
}
// ✅ Non-bot path: fetch the current Hosting HTML so it’s never stale
const htmlResp = await fetch(`https://${host}/index.html`, {
// prevent node-fetch from reusing cached responses
headers: { 'Cache-Control': 'no-cache' }
});
const html = await htmlResp.text();
// No-cache for HTML so users revalidate on each load
res.set('Cache-Control', 'no-cache, no-store, must-revalidate');
res.set('Pragma', 'no-cache');
res.set('Expires', '0');
return res.status(htmlResp.status || 200).send(html);
});
export const prerender = functions.https.onRequest(app);