-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.js
154 lines (143 loc) · 5.23 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
148
149
150
151
152
153
154
import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'
/**
* The DEBUG flag will do two things that help during development:
* 1. we will skip caching on the edge, which makes it easier to
* debug.
* 2. we will return an error message on exception in your Response rather
* than the default 404.html page.
*/
const DEBUG = false
/* cloudflare event listener + handler */
addEventListener('fetch', function(event) {
try {
const response = handleEvent(event) // pass event to handleEvent()
event.respondWith(response)
} catch (e) {
if (DEBUG) {
return event.respondWith(
new Response(e.message || e.toString(), {
status: 500,
}),
)
}
event.respondWith(new Response('Internal Error', { status: 500 }))
}
})
/**
* Receives a HTTP request and replies with a response.
* @param {Request} request
* @returns {Promise<Response>}
*/
async function handleEvent(event) {
const { request } = event
const { url } = request
const { host, pathname } = new URL(url)
let options = {}
if(pathname == '/' || pathname == '/index.html') {
// bypass cache for main page
options.cacheControl = {
browserTTL: 0,
edgeTTL: 0,
bypassCache: true
}
} else if (DEBUG) {
// bypass cache if in debug mode
options.cacheControl = {
bypassCache: true,
}
}
switch (pathname) {
case '/robots.txt':
return new Response(null, { status: 204 })
break;
default:
try {
// get requested resource from KV cache
const page = await getAssetFromKV(event, options)
// allow headers to be altered
const response = new Response(page.body, page)
// add some headers
response.headers.set('X-XSS-Protection', '1; mode=block')
response.headers.set('X-Content-Type-Options', 'nosniff')
response.headers.set('X-Frame-Options', 'DENY')
response.headers.set('Referrer-Policy', 'strict-origin')
response.headers.set('Permissions-Policy', 'none')
// if the main page / or /index is requested, we apply a HTMLRewriter to inject the IP-Address and location info
if(pathname == '/' || pathname == '/index.html') {
let ipInfo = getClientIPInfo(request)
let rewrittenResponse = new HTMLRewriter()
.on('input', new ElementHandler(ipInfo))
.on('h1', new ElementHandler())
.transform(response)
return rewrittenResponse
}
return response
} catch(e) {
// if an error is thrown try to serve the asset at 404.html
if (!DEBUG) {
try {
let notFoundResponse = await getAssetFromKV(request, {
mapRequestToAsset: req => new Request(`${new URL(req.url).origin}/404.html`, req),
})
return new Response(notFoundResponse.body, { ...notFoundResponse, status: 404 })
} catch (e) {}
}
return new Response(e.message || e.toString(), { status: 500 })
}
}
}
function getClientIPInfo(request) {
/* get clientdata from cloudflare */
/* the ||-comparison is to prevent errors in the workers quick edit mode */
let location = (request.cf || {}).country
// if we know which city, prepend to location
if((request.cf || {}).city) {
location = ((request.cf || {}).city)+', '+location
}
const clientIPInfo = {
/* get client ip address by Cloudflare header 'CF-Connecting-IP' */
ipaddress: request.headers.get('CF-Connecting-IP'),
/* location of client ip address */
location: location
}
return clientIPInfo
}
/* handles elements to inject values using HTMLRewriter */
class ElementHandler {
constructor(ipInfo=null) {
this.ipInfo = ipInfo
}
element(element) {
// get element id
const elementid = element.getAttribute('id')
// some debug output
if(DEBUG) {
console.log(`Incoming element: ${element.tagName} - id ${elementid}`)
}
switch(element.tagName) {
case 'h1':
if(DEBUG) {
element.append(' DEBUG MODE')
element.setAttribute('style','color: #ff0000')
}
if(ENVIRONMENT==="dev") {
element.append(' { DEV ENVIRONMENT }')
element.setAttribute('style','color: #eb7171')
}
break;
case 'input':
// depending on which element-id we have, put ipaddress or location in
switch(elementid) {
case 'ipaddress':
// set value of ipaddress-field in HTML
element.setAttribute('value',this.ipInfo.ipaddress)
break;
case 'location':
// set value of location-field in HTML
element.setAttribute('value',this.ipInfo.location)
break;
}
break;
}
}
}