This repository has been archived by the owner on Mar 29, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
147 lines (132 loc) · 4.2 KB
/
index.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
141
142
143
144
145
146
147
import srcset from 'srcset';
// import bees style and script for injecting, as raw minimised strings
import beeCss from 'raw-loader!postcss-loader!./assets/bees.css';
import beeJs from 'raw-loader!terser-loader!./assets/bees.js';
class BodyHandler {
constructor(nonce){
this.nonce = nonce;
}
element(element){
element.append(`<style>${beeCss}</style><script nonce="${this.nonce}">${beeJs}</script>`, {
html: true
});
}
}
// this handles rewriting of `/cdn-cgi` image src/srcset attributes since these can't be proxied in a worker
const imageResizeRegex = /^\/cdn-cgi\/image\/(?:[A-Za-z]+=[\dA-Za-z]+,?)+\/(.*)/;
class ImageHandler {
element(element){
const src = element.getAttribute('src');
if(src && src.startsWith('/cdn-cgi')){
const test = imageResizeRegex.exec(src);
if(test && test[1]){
element.setAttribute('src', test[1]);
}
}
const srcsetVal = element.getAttribute('srcset');
if(srcsetVal && srcsetVal.includes('/cdn-cgi')){
// quick and dirty override for all srcset images. Not a perfect solution, but good enough for this joke
const updatedImages = srcset.parse(srcsetVal).map((image) => {
const test = imageResizeRegex.exec(image.url);
if(test && test[1]){
image.url = test[1];
}
return image;
});
element.setAttribute('srcset', srcset.stringify(updatedImages));
}
}
}
class MetaHandler {
element(element){
// rewrite robots to ensure no indexing
const type = element.getAttribute('name');
if(type && type === 'robots'){
element.setAttribute('content', 'noindex, nofollow');
return;
}
// rewrite share URLs to make sure social links land on the right URL
const content = element.getAttribute('content');
if(content && content === 'https://nodecraft.com'){
element.setAttribute('content', 'https://bees.nodecraft.workers.dev');
}
}
}
// paths we're okay to add bees to
const validPaths = [{
path: '/games',
exact: false
},
{
path: '/community',
exact: false
},
{
path: '/blog',
exact: false
},
{
path: '/support',
exact: false
},
{
path: '/about',
exact: false
},
{
path: '/',
exact: true
}];
/* global HTMLRewriter */
async function handleRequest(request){
const url = new URL(request.url);
url.host = 'nodecraft.com';
const nodecraft = await fetch(url);
// if HTML, check if we should add bees, or redirect away
if(nodecraft.headers.has('content-type') && nodecraft.headers.get('content-type').includes('text/html')){
let redirectAway = true;
for(const path of validPaths){
if(url.pathname.startsWith(path.path)){
// in a match, now check if exact
if(path.exact && url.pathname === path.path){
redirectAway = false;
break;
}
if(!path.exact){
redirectAway = false;
}
}
}
if(redirectAway){
return Response.redirect(url, 302);
}
// get nonce for CSP JS
const nonce = await (await fetch('https://uuid.rocks/plain')).text();
// setup htmlrewriter
// handle our real script injection
const transformedRaw = new HTMLRewriter().on('body', new BodyHandler(nonce)).transform(nodecraft);
// rewrite images with resizing urls, since these don't work being proxieed
const transformedImages = new HTMLRewriter().on('img', new ImageHandler()).transform(transformedRaw);
// rewrite meta tags
const transformed = new HTMLRewriter().on('meta', new MetaHandler()).transform(transformedImages);
// get headers and fix up CSP
const headers = new Headers(transformed.headers);
// add our JS nonce for the bees script, and manipulate a few other directives to not break things
const CSP = headers.get('Content-Security-Policy');
let newCSP = CSP.replace('script-src', 'script-src \'nonce-' + nonce + '\'');
newCSP = newCSP.replace('default-src', 'default-src nodecraft.com');
newCSP = newCSP.replace('img-src', 'img-src nodecraft.com');
newCSP = newCSP.replace('font-src', 'font-src nodecraft.com');
headers.set('Content-Security-Policy', newCSP);
// we really don't want this page indexed
headers.set('X-Robots-Tag', 'noindex, nofollow');
return new Response(await transformed.body, {
headers: headers
});
}
// otherwise pass through requests (JS/CSS assets, etc.)
return nodecraft;
}
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request));
});