· 5 years ago · Jan 12, 2021, 12:30 AM
1/* CONFIGURATION STARTS HERE */
2
3 /* Step 1: enter your domain name like fruitionsite.com */
4 const MY_DOMAIN = 'wiki.advanced-roleplay.com.br';
5
6 /*
7 * Step 2: enter your URL slug to page ID mapping
8 * The key on the left is the slug (without the slash)
9 * The value on the right is the Notion page ID
10 */
11 const SLUG_TO_PAGE = {
12 '': '34e8a0135f1541b8852e15606d043f8d',
13 'primeiros-passos': '8b0b1184b4824c7e9514f0219c9f5013',
14 'veiculos': '7fdae73055324e00acd32ad6c5ad2d13',
15 'wphone': 'a0773d22570644a780676741669062f7'
16 };
17
18 /* Step 3: enter your page title and description for SEO purposes */
19 const PAGE_TITLE = '';
20 const PAGE_DESCRIPTION = '';
21
22 /* Step 4: enter a Google Font name, you can choose from https://fonts.google.com */
23 const GOOGLE_FONT = '';
24
25 /* Step 5: enter any custom scripts you'd like */
26 const CUSTOM_SCRIPT = ``;
27
28 /* CONFIGURATION ENDS HERE */
29
30const PAGE_TO_SLUG = {};
31const slugs = [];
32const pages = [];
33Object.keys(SLUG_TO_PAGE).forEach(slug => {
34 const page = SLUG_TO_PAGE[slug];
35 slugs.push(slug);
36 pages.push(page);
37 PAGE_TO_SLUG[page] = slug;
38});
39
40addEventListener("fetch", event => {
41 event.respondWith(fetchAndApply(event.request));
42});
43
44function generateSitemap() {
45 let sitemap = '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
46 slugs.forEach(
47 (slug) =>
48 (sitemap +=
49 "<url><loc>https://" + MY_DOMAIN + "/" + slug + "</loc></url>")
50 );
51 sitemap += "</urlset>";
52 return sitemap;
53}
54
55const corsHeaders = {
56 "Access-Control-Allow-Origin": "*",
57 "Access-Control-Allow-Methods": "GET, HEAD, POST, PUT, OPTIONS",
58 "Access-Control-Allow-Headers": "Content-Type"
59};
60
61function handleOptions(request) {
62 if (
63 request.headers.get("Origin") !== null &&
64 request.headers.get("Access-Control-Request-Method") !== null &&
65 request.headers.get("Access-Control-Request-Headers") !== null
66 ) {
67 // Handle CORS pre-flight request.
68 return new Response(null, {
69 headers: corsHeaders
70 });
71 } else {
72 // Handle standard OPTIONS request.
73 return new Response(null, {
74 headers: {
75 Allow: "GET, HEAD, POST, PUT, OPTIONS"
76 }
77 });
78 }
79}
80
81async function fetchAndApply(request) {
82 if (request.method === "OPTIONS") {
83 return handleOptions(request);
84 }
85 let url = new URL(request.url);
86 url.hostname = 'www.notion.so';
87 if (url.pathname === "/robots.txt") {
88 return new Response("Sitemap: https://" + MY_DOMAIN + "/sitemap.xml");
89 }
90 if (url.pathname === "/sitemap.xml") {
91 let response = new Response(generateSitemap());
92 response.headers.set("content-type", "application/xml");
93 return response;
94 }
95 let response;
96 if (url.pathname.startsWith("/app") && url.pathname.endsWith("js")) {
97 response = await fetch(url.toString());
98 let body = await response.text();
99 response = new Response(
100 body
101 .replace(/www.notion.so/g, MY_DOMAIN)
102 .replace(/notion.so/g, MY_DOMAIN),
103 response
104 );
105 response.headers.set("Content-Type", "application/x-javascript");
106 return response;
107 } else if (url.pathname.startsWith("/api")) {
108 // Forward API
109 response = await fetch(url.toString(), {
110 body: request.body,
111 headers: {
112 "content-type": "application/json;charset=UTF-8",
113 "user-agent":
114 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"
115 },
116 method: "POST"
117 });
118 response = new Response(response.body, response);
119 response.headers.set("Access-Control-Allow-Origin", "*");
120 return response;
121 } else if (slugs.indexOf(url.pathname.slice(1)) > -1) {
122 const pageId = SLUG_TO_PAGE[url.pathname.slice(1)];
123 return Response.redirect("https://" + MY_DOMAIN + "/" + pageId, 301);
124 } else {
125 response = await fetch(url.toString(), {
126 body: request.body,
127 headers: request.headers,
128 method: request.method
129 });
130 response = new Response(response.body, response);
131 response.headers.delete("Content-Security-Policy");
132 response.headers.delete("X-Content-Security-Policy");
133 }
134
135 return appendJavascript(response, SLUG_TO_PAGE);
136}
137
138class MetaRewriter {
139 element(element) {
140 if (PAGE_TITLE !== "") {
141 if (
142 element.getAttribute("property") === "og:title" ||
143 element.getAttribute("name") === "twitter:title"
144 ) {
145 element.setAttribute("content", PAGE_TITLE);
146 }
147 if (element.tagName === "title") {
148 element.setInnerContent(PAGE_TITLE);
149 }
150 }
151 if (PAGE_DESCRIPTION !== "") {
152 if (
153 element.getAttribute("name") === "description" ||
154 element.getAttribute("property") === "og:description" ||
155 element.getAttribute("name") === "twitter:description"
156 ) {
157 element.setAttribute("content", PAGE_DESCRIPTION);
158 }
159 }
160 if (
161 element.getAttribute("property") === "og:url" ||
162 element.getAttribute("name") === "twitter:url"
163 ) {
164 element.setAttribute("content", MY_DOMAIN);
165 }
166 if (element.getAttribute("name") === "apple-itunes-app") {
167 element.remove();
168 }
169 }
170}
171
172class HeadRewriter {
173 element(element) {
174 if (GOOGLE_FONT !== "") {
175 element.append(
176 `<link href='https://fonts.googleapis.com/css?family=${GOOGLE_FONT.replace(' ', '+')}:Regular,Bold,Italic&display=swap' rel='stylesheet'>
177 <style>* { font-family: "${GOOGLE_FONT}" !important; }</style>`,
178 {
179 html: true
180 }
181 );
182 }
183 element.append(
184 `<style>
185 div.notion-topbar > div > div:nth-child(3) { display: none !important; }
186 div.notion-topbar > div > div:nth-child(4) { display: none !important; }
187 div.notion-topbar > div > div:nth-child(5) { display: none !important; }
188 div.notion-topbar > div > div:nth-child(6) { display: none !important; }
189 div.notion-topbar-mobile > div:nth-child(3) { display: none !important; }
190 div.notion-topbar-mobile > div:nth-child(4) { display: none !important; }
191 div.notion-topbar > div > div:nth-child(1n).toggle-mode { display: block !important; }
192 div.notion-topbar-mobile > div:nth-child(1n).toggle-mode { display: block !important; }
193 </style>`,
194 {
195 html: true
196 }
197 );
198 }
199}
200
201class BodyRewriter {
202 constructor(SLUG_TO_PAGE) {
203 this.SLUG_TO_PAGE = SLUG_TO_PAGE;
204 }
205 element(element) {
206 element.append(
207 `<script>
208 const SLUG_TO_PAGE = ${JSON.stringify(this.SLUG_TO_PAGE)};
209 const PAGE_TO_SLUG = {};
210 const slugs = [];
211 const pages = [];
212 const el = document.createElement('div');
213 let redirected = false;
214 Object.keys(SLUG_TO_PAGE).forEach(slug => {
215 const page = SLUG_TO_PAGE[slug];
216 slugs.push(slug);
217 pages.push(page);
218 PAGE_TO_SLUG[page] = slug;
219 });
220 function getPage() {
221 return location.pathname.slice(-32);
222 }
223 function getSlug() {
224 return location.pathname.slice(1);
225 }
226 function updateSlug() {
227 const slug = PAGE_TO_SLUG[getPage()];
228 if (slug != null) {
229 history.replaceState(history.state, '', '/' + slug);
230 }
231 }
232 function onDark() {
233 el.innerHTML = '<div title="Change to Light Mode" style="margin-left: auto; margin-right: 14px; min-width: 0px;"><div role="button" tabindex="0" style="user-select: none; transition: background 120ms ease-in 0s; cursor: pointer; border-radius: 44px;"><div style="display: flex; flex-shrink: 0; height: 14px; width: 26px; border-radius: 44px; padding: 2px; box-sizing: content-box; background: rgb(46, 170, 220); transition: background 200ms ease 0s, box-shadow 200ms ease 0s;"><div style="width: 14px; height: 14px; border-radius: 44px; background: white; transition: transform 200ms ease-out 0s, background 200ms ease-out 0s; transform: translateX(12px) translateY(0px);"></div></div></div></div>';
234 document.body.classList.add('dark');
235 __console.environment.ThemeStore.setState({ mode: 'dark' });
236 };
237 function onLight() {
238 el.innerHTML = '<div title="Change to Dark Mode" style="margin-left: auto; margin-right: 14px; min-width: 0px;"><div role="button" tabindex="0" style="user-select: none; transition: background 120ms ease-in 0s; cursor: pointer; border-radius: 44px;"><div style="display: flex; flex-shrink: 0; height: 14px; width: 26px; border-radius: 44px; padding: 2px; box-sizing: content-box; background: rgba(135, 131, 120, 0.3); transition: background 200ms ease 0s, box-shadow 200ms ease 0s;"><div style="width: 14px; height: 14px; border-radius: 44px; background: white; transition: transform 200ms ease-out 0s, background 200ms ease-out 0s; transform: translateX(0px) translateY(0px);"></div></div></div></div>';
239 document.body.classList.remove('dark');
240 __console.environment.ThemeStore.setState({ mode: 'light' });
241 }
242 function toggle() {
243 if (document.body.classList.contains('dark')) {
244 onLight();
245 } else {
246 onDark();
247 }
248 }
249 function addDarkModeButton(device) {
250 const nav = device === 'web' ? document.querySelector('.notion-topbar').firstChild : document.querySelector('.notion-topbar-mobile');
251 el.className = 'toggle-mode';
252 el.addEventListener('click', toggle);
253 nav.appendChild(el);
254 onLight();
255 }
256 const observer = new MutationObserver(function() {
257 if (redirected) return;
258 const nav = document.querySelector('.notion-topbar');
259 const mobileNav = document.querySelector('.notion-topbar-mobile');
260 if (nav && nav.firstChild && nav.firstChild.firstChild
261 || mobileNav && mobileNav.firstChild) {
262 redirected = true;
263 updateSlug();
264 addDarkModeButton(nav ? 'web' : 'mobile');
265 const onpopstate = window.onpopstate;
266 window.onpopstate = function() {
267 if (slugs.includes(getSlug())) {
268 const page = SLUG_TO_PAGE[getSlug()];
269 if (page) {
270 history.replaceState(history.state, 'bypass', '/' + page);
271 }
272 }
273 onpopstate.apply(this, [].slice.call(arguments));
274 updateSlug();
275 };
276 }
277 });
278 observer.observe(document.querySelector('#notion-app'), {
279 childList: true,
280 subtree: true,
281 });
282 const replaceState = window.history.replaceState;
283 window.history.replaceState = function(state) {
284 if (arguments[1] !== 'bypass' && slugs.includes(getSlug())) return;
285 return replaceState.apply(window.history, arguments);
286 };
287 const pushState = window.history.pushState;
288 window.history.pushState = function(state) {
289 const dest = new URL(location.protocol + location.host + arguments[2]);
290 const id = dest.pathname.slice(-32);
291 if (pages.includes(id)) {
292 arguments[2] = '/' + PAGE_TO_SLUG[id];
293 }
294 return pushState.apply(window.history, arguments);
295 };
296 const open = window.XMLHttpRequest.prototype.open;
297 window.XMLHttpRequest.prototype.open = function() {
298 arguments[1] = arguments[1].replace('${MY_DOMAIN}', 'www.notion.so');
299 return open.apply(this, [].slice.call(arguments));
300 };
301 </script>${CUSTOM_SCRIPT}`,
302 {
303 html: true
304 }
305 );
306 }
307}
308
309async function appendJavascript(res, SLUG_TO_PAGE) {
310 return new HTMLRewriter()
311 .on("title", new MetaRewriter())
312 .on("meta", new MetaRewriter())
313 .on("head", new HeadRewriter())
314 .on("body", new BodyRewriter(SLUG_TO_PAGE))
315 .transform(res);
316}