Skip to content

Commit ef565f4

Browse files
committed
chore: reduce code duplication in Cloudflare Workers demo
- Replace 4 static HTML files with a shared template in server.js - Merge ROUTE_LOCATION_MAP and feature key constants into a single ROUTES config object - Deduplicate statistics/pricing experiment logic in decideForRoute() by branching on experienceKey vs runExperiences Addresses SonarCloud duplicated lines threshold (target ≤ 3%).
1 parent 0586c38 commit ef565f4

6 files changed

Lines changed: 142 additions & 293 deletions

File tree

demo/cloudflare-workers/origin/pages/events.html

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

demo/cloudflare-workers/origin/pages/index.html

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

demo/cloudflare-workers/origin/pages/pricing.html

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

demo/cloudflare-workers/origin/pages/statistics.html

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

demo/cloudflare-workers/origin/server.js

Lines changed: 106 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,123 @@
11
/*!
22
* Simple origin server for the Cloudflare Workers demo.
3-
* Serves static HTML pages that the Worker proxies and modifies.
3+
* Generates HTML pages from a shared template that the Worker proxies and modifies.
44
*
55
* Usage: node origin/server.js
66
* Serves on http://localhost:8888
77
*/
88

99
const http = require('http');
10-
const fs = require('fs');
11-
const path = require('path');
1210

1311
const PORT = 8888;
14-
const PAGES_DIR = path.join(__dirname, 'pages');
12+
13+
// Page definitions — only the unique content per route
14+
const PAGES = {
15+
'/': {
16+
title: 'Convert Edge Demo',
17+
body: `
18+
<div class="card">
19+
<h1>Cloudflare Workers Demo</h1>
20+
<p>This page is served by a simple origin server and proxied through a Cloudflare Worker.</p>
21+
<p>The Worker runs Convert A/B tests at the edge and modifies HTML before delivery — zero flicker.</p>
22+
</div>
23+
<div class="card" style="background:#e8f4fd;border-left:4px solid #2196F3">
24+
<strong>Tip:</strong> Visit the <a href="/events">Events</a>, <a href="/statistics">Statistics</a>,
25+
or <a href="/pricing">Pricing</a> pages to see experiments in action.
26+
The home page has no experiments configured.
27+
</div>`
28+
},
29+
'/events': {
30+
title: 'Events - Convert Edge Demo',
31+
heading: 'Events',
32+
description: 'Experience: <code>test-experience-ab-fullstack-1</code> | Location: <code>events</code>',
33+
featureLabel: 'Feature Status'
34+
},
35+
'/statistics': {
36+
title: 'Statistics - Convert Edge Demo',
37+
heading: 'Statistics',
38+
description: 'Runs all matching experiences | Feature: <code>feature-4</code> | Location: <code>statistics</code>',
39+
featureLabel: 'Feature Status (feature-4)'
40+
},
41+
'/pricing': {
42+
title: 'Pricing - Convert Edge Demo',
43+
heading: 'Pricing',
44+
description:
45+
'Runs all matching experiences | Feature: <code>feature-5</code> | Location: <code>pricing</code></p>' +
46+
'<p>Experiences on this route: <code>test-experience-ab-fullstack-1</code> and <code>test-experience-ab-fullstack-4</code>',
47+
featureLabel: 'Feature Status (feature-5)'
48+
}
49+
};
50+
51+
// Shared layout template
52+
function renderPage(page) {
53+
const nav = `<nav>
54+
<a href="/">Home</a>
55+
<a href="/events">Events</a>
56+
<a href="/statistics">Statistics</a>
57+
<a href="/pricing">Pricing</a>
58+
</nav>`;
59+
60+
// Home page uses custom body; experiment pages use a standard layout
61+
const content = page.body || `
62+
<div class="card">
63+
<h1>${page.heading}</h1>
64+
<p>${page.description}</p>
65+
</div>
66+
<div class="card">
67+
<h3>Experiment Results</h3>
68+
<div id="experiment-results">
69+
<span class="placeholder">No experiment bucketing yet — this content is replaced by the Worker.</span>
70+
</div>
71+
</div>
72+
<div class="card">
73+
<h3>${page.featureLabel}</h3>
74+
<div id="feature-status">
75+
<span class="placeholder">No feature flag evaluated.</span>
76+
</div>
77+
</div>
78+
<div class="card">
79+
<h3>Variation Caption</h3>
80+
<div id="variation-caption">
81+
<span class="placeholder">Original caption (not modified)</span>
82+
</div>
83+
</div>`;
84+
85+
return `<!DOCTYPE html>
86+
<html lang="en">
87+
<head>
88+
<meta charset="UTF-8">
89+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
90+
<title>${page.title}</title>
91+
<style>
92+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background: #f5f5f5; }
93+
nav { background: #333; padding: 10px 20px; border-radius: 8px; margin-bottom: 20px; }
94+
nav a { color: #fff; text-decoration: none; margin-right: 20px; }
95+
nav a:hover { text-decoration: underline; }
96+
.card { background: #fff; border-radius: 8px; padding: 20px; margin-bottom: 16px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
97+
h1 { color: #333; }
98+
.placeholder { color: #999; font-style: italic; }
99+
.enabled { color: #4CAF50; font-weight: bold; }
100+
</style>
101+
</head>
102+
<body>
103+
${nav}
104+
${content}
105+
</body>
106+
</html>`;
107+
}
15108

16109
const server = http.createServer((req, res) => {
17-
// Map URL path to HTML file
18-
let filePath;
19-
const pathname = req.url.split('?')[0]; // strip query string
20-
21-
switch (pathname) {
22-
case '/':
23-
filePath = path.join(PAGES_DIR, 'index.html');
24-
break;
25-
case '/events':
26-
filePath = path.join(PAGES_DIR, 'events.html');
27-
break;
28-
case '/statistics':
29-
filePath = path.join(PAGES_DIR, 'statistics.html');
30-
break;
31-
case '/pricing':
32-
filePath = path.join(PAGES_DIR, 'pricing.html');
33-
break;
34-
default:
35-
res.writeHead(404, {'Content-Type': 'text/html'});
36-
res.end('<h1>404 Not Found</h1>');
37-
return;
110+
const pathname = req.url.split('?')[0];
111+
const page = PAGES[pathname];
112+
113+
if (!page) {
114+
res.writeHead(404, {'Content-Type': 'text/html'});
115+
res.end('<h1>404 Not Found</h1>');
116+
return;
38117
}
39118

40-
fs.readFile(filePath, 'utf8', (err, data) => {
41-
if (err) {
42-
res.writeHead(500, {'Content-Type': 'text/plain'});
43-
res.end('Internal Server Error');
44-
return;
45-
}
46-
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
47-
res.end(data);
48-
});
119+
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
120+
res.end(renderPage(page));
49121
});
50122

51123
server.listen(PORT, () => {

0 commit comments

Comments
 (0)