Skip to content

Commit 3668f9e

Browse files
[Fix] Blank page shown when opening URL directly (e.g. domain/registration) #548
[Fix] Blank page shown when opening URL directly (e.g. domain/registration)
2 parents efc1e07 + d866210 commit 3668f9e

File tree

6 files changed

+130
-115
lines changed

6 files changed

+130
-115
lines changed

firebase.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,6 @@
4444
"value": "same-origin"
4545
}
4646
]
47-
},
48-
{
49-
"source": "/@(service-worker|firebase-messaging-sw).js",
50-
"headers": [
51-
{
52-
"key": "Cache-Control",
53-
"value": "no-cache, no-store, must-revalidate"
54-
}
55-
]
5647
}
5748
]
5849
},

functions/src/prerender.ts

Lines changed: 46 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,34 @@ import express from 'express';
44
import { getFirestore } from 'firebase-admin/firestore';
55
import * as functions from 'firebase-functions';
66
import fetch from 'node-fetch';
7-
import url, { URL } from 'url';
87

98
const app = express();
109

11-
const getSiteDomain = async () => {
12-
const doc = await getFirestore().collection('config').doc('site').get();
13-
return doc.data().domain;
14-
};
15-
1610
const getRendertronServer = async () => {
1711
const doc = await getFirestore().collection('config').doc('rendertron').get();
1812
return doc.data().server;
1913
};
2014

21-
/**
22-
* generateUrl() - Piece together request parts to form FQDN URL
23-
* @param {Object} request
24-
*/
25-
const generateUrl = async (request) => {
26-
// Why do we use config site.domain instead of the domain from
27-
// the request? Because it'll give you the wrong domain (pointed at the
28-
// cloudfunctions.net)
29-
return url.format({
30-
protocol: request.protocol,
31-
host: await getSiteDomain(),
32-
pathname: request.originalUrl,
33-
});
15+
const normalizeHost = (h: string) =>
16+
h.replace(/^https?:\/\//, '').replace(/\/+$/, '');
17+
18+
const resolveHost = (req): string => {
19+
// 1. Prefer Hosting/CDN header
20+
const headerHost =
21+
req.get('x-forwarded-host') || req.get('host') || req.hostname;
22+
23+
if (headerHost) {
24+
return normalizeHost(headerHost);
25+
}
26+
27+
// 2. Fallback to Firebase config
28+
const cfgHost = functions.config()?.site?.domain as string | undefined;
29+
if (cfgHost) {
30+
return normalizeHost(cfgHost);
31+
}
32+
33+
// 3. Last fallback (safe default for dev)
34+
return 'localhost:5000';
3435
};
3536

3637
/**
@@ -55,38 +56,35 @@ const checkForBots = (userAgent) => {
5556
//
5657
// The trick is on L66, pwaShell(): You must update that file! Open for explainer.
5758
app.get('*', async (req, res) => {
58-
// What say you bot tester?
59+
const host = resolveHost(req);
5960
const botResult = checkForBots(req.headers['user-agent']);
61+
6062
if (botResult) {
61-
// Get me the url all nice
62-
const targetUrl = generateUrl(req);
63-
64-
// Did you read the README? You should have set rendertron document
65-
// to where ever you deployed https://github.com/GoogleChrome/rendertron on AppEngine
66-
fetch(`${await getRendertronServer()}/render/${targetUrl}`)
67-
.then((res) => res.text())
68-
.then((body) => {
69-
// We set Vary because we only want to cache this result for the bots
70-
// which we know based on the user-agent. Vary is very useful.
71-
// Reading about Vary header:
72-
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary
73-
// https://www.fastly.com/blog/best-practices-using-vary-header/
74-
res.set('Cache-Control', 'public, max-age=300, s-maxage=600');
75-
res.set('Vary', 'User-Agent');
76-
77-
res.send(body.toString());
78-
});
79-
} else {
80-
// 1. Umm, Justin, why not just point to index.html?
81-
// 2. Umm, Justin, why not just fetch() index.html from the domain?
82-
//
83-
// Valid things to ask internet peoples
84-
// 1. function doesn't know about the public hosting as far as I can tell (docs don't offer opinion/example)
85-
// 2. Could fetch and return...but I found copy+paste the index.html PWA shell into file returns faster
86-
// const indexHTML = fs.readFileSync('./index.html').toString();
87-
const path = new URL('./index.html', import.meta.url).pathname;
88-
res.sendFile(path);
63+
// Bot path via Rendertron (you can keep your caching here if you want)
64+
const targetUrl = `https://${host}${req.originalUrl}`;
65+
const rendertron = await getRendertronServer();
66+
67+
const botResp = await fetch(`${rendertron}/render/${targetUrl}`);
68+
const body = await botResp.text();
69+
70+
res.set('Cache-Control', 'public, max-age=300, s-maxage=600');
71+
res.set('Vary', 'User-Agent');
72+
return res.send(body.toString());
8973
}
74+
75+
// ✅ Non-bot path: fetch the current Hosting HTML so it’s never stale
76+
const htmlResp = await fetch(`https://${host}/index.html`, {
77+
// prevent node-fetch from reusing cached responses
78+
headers: { 'Cache-Control': 'no-cache' }
79+
});
80+
const html = await htmlResp.text();
81+
82+
// No-cache for HTML so users revalidate on each load
83+
res.set('Cache-Control', 'no-cache, no-store, must-revalidate');
84+
res.set('Pragma', 'no-cache');
85+
res.set('Expires', '0');
86+
87+
return res.status(htmlResp.status || 200).send(html);
9088
});
9189

9290
export const prerender = functions.https.onRequest(app);

public/data/meet-the-talent.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,44 @@
3838
"name": "X/Twitter"
3939
}
4040
]
41+
},{
42+
"fullName": "Jaydip Umaretiya",
43+
"location": "Surat, Gujarat, India",
44+
"bio": "Experienced Android developer with 10+ years of expertise in building native mobile and TV applications, including digital signage solutions. Proficient in Kotlin, Java, Jetpack Compose, and native libraries, with strong skills in creating modern, responsive UIs. Well-versed in Kotlin Multiplatform (KMP) and Compose Multiplatform (CMP) to deliver scalable cross-platform solutions. Adept at project leadership, team mentoring, and managing complex, high-impact applications. Committed to innovation, exceeding client expectations, and fostering long-term partnerships.",
45+
"email": "jaynumaretiya@gmail.com",
46+
"phone": "+91-9510086772",
47+
"imageUrl": "https://www.jaydip.tech/static/media/img-mobile.ec99727dfdae5c2cdd64.png",
48+
"website": "https://jaydip.tech/",
49+
"skills": [
50+
"Android",
51+
"Kotlin",
52+
"Jetpack Compose",
53+
"Android TV Development",
54+
"Kotlin MultiPlatform",
55+
"Compose MultiPlatform"
56+
],
57+
"links": [
58+
{
59+
"icon": "website",
60+
"link": "https://jaydip.tech/",
61+
"name": "Portfolio"
62+
},
63+
{
64+
"icon": "github",
65+
"link": "https://github.com/jaydipumaretiya",
66+
"name": "Github"
67+
},
68+
{
69+
"icon": "linkedin",
70+
"link": "https://www.linkedin.com/in/jaydipumaretiya",
71+
"name": "LinkedIn"
72+
},
73+
{
74+
"icon": "twitter",
75+
"link": "https://x.com/jaydipumaretiya",
76+
"name": "X/Twitter"
77+
}
78+
]
4179
}
4280
]
4381
}

rollup.config.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,6 @@ import { compileBufferTemplate, production } from './utils/build';
1313
const { ROLLUP_WATCH } = process.env;
1414

1515
export default [
16-
{
17-
input: 'src/firebase-messaging-sw.ts',
18-
treeshake: production,
19-
output: {
20-
file: 'dist/firebase-messaging-sw.js',
21-
sourcemap: production,
22-
},
23-
plugins: [
24-
nodeResolve(),
25-
typescript({
26-
noEmitOnError: true,
27-
sourceMap: production,
28-
}),
29-
production && terser(),
30-
],
31-
},
3216
{
3317
treeshake: production,
3418
output: {

src/firebase-messaging-sw.ts

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/pages/registration-page.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,39 @@ export class RegistrationPage extends PolymerElement {
240240
font-weight: 700;
241241
padding-bottom: 8px;
242242
}
243+
244+
.ticket-coupon-info {
245+
margin: 32px 0;
246+
padding: 24px;
247+
background-color: #fef7f7;
248+
border-left: 6px solid #e53935;
249+
border-radius: 10px;
250+
font-size: 16px;
251+
line-height: 1.6;
252+
color: #333;
253+
}
254+
255+
.ticket-coupon-info strong {
256+
font-weight: bold;
257+
}
258+
259+
.ticket-coupon-info ul {
260+
list-style: none;
261+
padding-left: 8px;
262+
margin: 12px 0;
263+
}
264+
265+
.ticket-coupon-info ul li::before {
266+
content: '';
267+
margin-right: 8px;
268+
}
269+
270+
@media (max-width: 768px) {
271+
.ticket-coupon-info {
272+
font-size: 15px;
273+
padding: 20px;
274+
}
275+
}
243276
</style>
244277
245278
<simple-hero page="registration"></simple-hero>
@@ -408,6 +441,19 @@ export class RegistrationPage extends PolymerElement {
408441
<p>This helps us avoid overcrowding and ensures that everyone gets the most out of the sessions they’re truly
409442
passionate about.</p>
410443
</div>
444+
<section class="ticket-coupon-info">
445+
<p>🚫 <strong>Coupon code? Seriously?!</strong></p>
446+
<p>🎟️ અમારી ટિકિટની કિંમત પહેલાંથી જ community ને ધ્યાનમાં રાખીને રાખવામાં આવી છે — it's already a highly subsidized price considering the amazing value you get!</p>
447+
<ul>
448+
<li>🍱 Delicious food.</li>
449+
<li>👕 Swags you’ll love.</li>
450+
<li>🧠 Expert talks.</li>
451+
<li>💡 Immense learning.</li>
452+
<li>🎉 And a full-day networking with like-minded tech folks.</li>
453+
</ul>
454+
<p>💬 Coupon code માંગીને શરમાવશો નહીં — એક દિવસ પિઝ્ઝા પાર્ટી ના ખર્ચે, તમારું ભવિષ્ય સુધારવાની તક છે.</p>
455+
<p>👉 <strong>Knowledge મા investment કરશો, regret નહીં થાય.</strong></p>
456+
</section>
411457
<section class="ticket-types">
412458
<h1>Ticket Types</h1>
413459
<div class="ticket-container">

0 commit comments

Comments
 (0)