Skip to content

Instantly share code, notes, and snippets.

@mayneyao
Last active September 14, 2024 01:22
Show Gist options
  • Save mayneyao/b9fefc9625b76f70488e5d8c2a99315d to your computer and use it in GitHub Desktop.
Save mayneyao/b9fefc9625b76f70488e5d8c2a99315d to your computer and use it in GitHub Desktop.
Notion.so > Personal Blog | custom domain + disqus comment
const MY_DOMAIN = "agodrich.com"
const START_PAGE = "https://www.notion.so/gatsby-starter-notion-2c5e3d685aa341088d4cd8daca52fcc2"
const DISQUS_SHORTNAME = "agodrich"
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, HEAD, POST,PUT, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
}
function handleOptions(request) {
if (request.headers.get("Origin") !== null &&
request.headers.get("Access-Control-Request-Method") !== null &&
request.headers.get("Access-Control-Request-Headers") !== null) {
// Handle CORS pre-flight request.
return new Response(null, {
headers: corsHeaders
})
} else {
// Handle standard OPTIONS request.
return new Response(null, {
headers: {
"Allow": "GET, HEAD, POST, PUT, OPTIONS",
}
})
}
}
async function fetchAndApply(request) {
if (request.method === "OPTIONS") {
return handleOptions(request)
}
let url = new URL(request.url)
let response
if (url.pathname.startsWith("/app") && url.pathname.endsWith("js")) {
// skip validation in app.js
response = await fetch(`https://www.notion.so${url.pathname}`)
let body = await response.text()
try {
response = new Response(body.replace(/www.notion.so/g, MY_DOMAIN).replace(/notion.so/g, MY_DOMAIN), response)
response.headers.set('Content-Type', "application/x-javascript")
console.log("get rewrite app.js")
} catch (err) {
console.log(err)
}
} else if ((url.pathname.startsWith("/api"))) {
// Forward API
response = await fetch(`https://www.notion.so${url.pathname}`, {
body: request.body, // must match 'Content-Type' header
headers: {
'content-type': 'application/json;charset=UTF-8',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'
},
method: "POST", // *GET, POST, PUT, DELETE, etc.
})
response = new Response(response.body, response)
response.headers.set('Access-Control-Allow-Origin', "*")
} else if (url.pathname === `/`) {
// 301 redrict
let pageUrlList = START_PAGE.split("/")
let redrictUrl = `https://${MY_DOMAIN}/${pageUrlList[pageUrlList.length - 1]}`
return Response.redirect(redrictUrl, 301)
} else {
response = await fetch(`https://www.notion.so${url.pathname}`, {
body: request.body, // must match 'Content-Type' header
headers: request.headers,
method: request.method, // *GET, POST, PUT, DELETE, etc.
})
response = new Response(response.body, response)
if (DISQUS_SHORTNAME) {
// Delete CSP to load disqus content
response.headers.delete("Content-Security-Policy")
// add disqus comment component for every notion page
return new HTMLRewriter().on('body', new ElementHandler()).transform(response)
}
}
return response
}
class ElementHandler {
element(element) {
// An incoming element, such as `div`
element.append(`
<script>
let disqus = document.createElement("div")
disqus.id = "disqus_thread"
disqus.style.width = "100%"
var disqus_config = function () {
let pathList = window.location.pathname.split("-")
let pageID = pathList[pathList.length - 1]
this.page.url = window.location.href;
if (/^[\w]{32}$/.test(pageID)) {
this.page.identifier = pageID;
}else{
this.page.identifier = undefined;
}
};
(function () {
var d = document, s = d.createElement('script');
s.src = 'https://${DISQUS_SHORTNAME}.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
// if you want to hide some element, add the selector to hideEle Array
const hideEle = [
"#notion-app > div > div.notion-cursor-listener > div > div:nth-child(1) > div.notion-topbar > div > div:nth-child(6)",
"#notion-app > div > div.notion-cursor-listener > div > div:nth-child(1) > div.notion-topbar > div > div:nth-child(5)",
"#notion-app > div > div.notion-cursor-listener > div > div:nth-child(1) > div.notion-topbar > div > div:nth-child(4)",
]
// if you want to replace some element, add the selector and innerHTML to replaceEle Object
const replaceEle = {
"#notion-app > div > div.notion-cursor-listener > div > div:nth-child(1) > div.notion-topbar > div > div:nth-child(6)": "<span>agodrich<span>"
}
function hideElement(qs) {
let eles = document.querySelectorAll(qs)
eles && eles.forEach(ele => ele.style.display = "none")
}
function replaceElement(qs, _html) {
let ele = document.querySelector(qs)
if (ele) {
ele.innerHTML = _html
}
}
let MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
let body = document.querySelector('body');
let observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
let pageContent = document.querySelector("#notion-app div.notion-page-content")
if (pageContent) {
if (pageContent.lastChild && pageContent.lastChild.id !== "disqus_thread") {
pageContent.append(disqus)
DISQUS.reset({ reload: true })
console.log(+new Date())
}
}
hideEle.forEach( hideE => hideElement(hideE) )
Object.entries(replaceEle).forEach( item => {
let [qs,_html] = item;
replaceEle(qs,_html)
})
});
});
observer.observe(body, { subtree: true, childList: true });
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
`, { html: Boolean })
console.log(`Incoming element: ${element.tagName}`)
}
comments(comment) {
// An incoming comment
}
text(text) {
// An incoming piece of text
}
}
@lucaberta
Copy link

lucaberta commented May 8, 2020

Great job @stephenou!

I have copy-pasted the latest 3 comments by @edgarascom @olamoller @quicklywilliam in this gist as issues on your repo so they can be addressed there as part of the fruition repo at https://github.com/stephenou/fruitionsite, and not together with the original gist by @mayneyao.

I have just implemented your worker in a test scenario, replacing the previous one, and it worked immediately. Very happy for what you have achieved and shared, thanks!

@edgarasben
Copy link

Thanks @lucaberta!

@jdmaresco
Copy link

When I apply this code and use custom domain, the number of "Requests today" goes up very quickly.
("Requests today" is on Worker's Dashboard)

About 1,000 requests have been filled by me looking my domain 1 hour.
How I can solve this?

@StrongMentalCarp – how did you end up resolving this?

I'm having the same issue after setting up based on the instructions at https://fruitionsite.com/ (which is basically an interactive tool with source code forked from this gist). I'm getting about 1-2 RPS steady state. It feels like there's an infinite loop somewhere.

  • I have 3 DNS A records set up: (*, www, and a 3rd subdomain)
  • I have 2 routes (*.<domain>/* and <domain>/*)

@vasubhatia
Copy link

vasubhatia commented May 24, 2020

When I apply this code and use custom domain, the number of "Requests today" goes up very quickly.
("Requests today" is on Worker's Dashboard)
About 1,000 requests have been filled by me looking my domain 1 hour.
How I can solve this?

@StrongMentalCarp – how did you end up resolving this?

I'm having the same issue after setting up based on the instructions at https://fruitionsite.com/ (which is basically an interactive tool with source code forked from this gist). I'm getting about 1-2 RPS steady state. It feels like there's an infinite loop somewhere.

  • I have 3 DNS A records set up: (*, www, and a 3rd subdomain)
  • I have 2 routes (*.<domain>/* and <domain>/*)

You need to create the proper DNS records and set the script and rules accordingly. Here are the steps to follow:

  1. Delete all the existing DNS records
  2. Create this A record: Name: @ Content: 1.1.1.1 Click on the proxy status and select it as DNS ONLY (IMPORTANT)
  3. 2nd A record: Name: www Content: 1.1.1.1 The proxy status for this is supposed to be Proxied(enabled by default)
  4. Go to Page Rules and create a new page rule:
    (Assuming the site's name is example.com)
    a) Set the site name as example.com
    b) Select the rule type as Forwarding URL
    c) Select the status code as 301 Permanent redirect
    d) Enter the destination URL as https://www.example.com
  5. Use fruition to create the script, when it asks you for the domain, enter the domain as www.example.com (Follow the fruition instructions on how to create a worker and all if you haven't done that already)
  6. When you create the route, make sure you create the route for www.example.com/*

Now, when you type in example.com in the address bar, you site would be redirecting to http://www.example.com

@jdmaresco
Copy link

@vasubhatia thank you for chiming in. A few questions:

  • What's the logic for proxying some A records and not others?
  • I want a custom subdomain to be the canonical URL for the site (not www.), so www and root domains should redirect there
    • I previously changed MY_DOMAIN in the generated script to sub.domain.com because the Fruition generator doesn't support subdomains
  • What's the difference between using a page rule to forward to the site vs just creating multiple routes and pointing them all the worker?
  • Do you know what might be causing the looping I'm seeing?

@jamalx31
Copy link

Thanks for this.
i'm trying to follow the instractions from Fruition with sub domain but when but I keep getting DNS_PROBE_FINISHED_NXDOMAIN when try to access sub.domain.io

@fpenguin
Copy link

I'm getting "ERR_TOO_MANY_REDIRECTS" error. :-(

I did notice that for me, the URL changes from domain.com -> domain.com/NotionPageID when I try with www.domain.com
where as, if I try without www, then I see from the browser tab, the URL does not show the Notion Page ID.

Any tips?

@edgarasben
Copy link

Make sure to set both routes "www.yourdomain.com/" and "yourdomain.com/".

Also, if you redirected domain recently, it might jus take some days to update your domain properly. There can be cache problems for few days with your internet provider. Try with mobile internet.

@fpenguin
Copy link

I'm curious if I have my DNS settings incorrect.

From my registrar, I have 2 cloudflare nameservers : x.ns.cloudflare.com, y.ns.cloudflare.com

Do I need to have CNAME setup from cloudflare?
I originally had A and MX setup under the cloudflash DNS menu

A - mydomain.com - 1.1.1.1
MX - Google Apps for my emails

With 1.1.1.1 as the IP for A entry -> gave me error saying forbidden IP -> changed it to 220.181.38.148 which I saw from other Youtube video on custom notion domain -> but this time I get "Web server is returning an unknown error"

@edgarasben
Copy link

Nameservers sound right, if x and y is a correct word (I have different ones).

Regarding the DNS you don't need to set anything. If anything is set or empty it does not matter. The Workers ignore that.

@fpenguin
Copy link

fpenguin commented Jun 25, 2020

Thanks for help!

I was able to get rid of "Error 520 - Web server is returning an unknown error" by changing the Route setting.

BEFORE (single entry)
*.mydomain.com/*

AFTER (two entries)

mydomain.com/*
www.mydomain.com/*

@lucaberta
Copy link

Make sure to set both routes "www.yourdomain.com/" and "yourdomain.com/".

you can achieve the same result by having one single rule using an asterisk, like I mentioned in this past comment:

https://gist.github.com/mayneyao/b9fefc9625b76f70488e5d8c2a99315d#gistcomment-3095515

@fpenguin
Copy link

Make sure to set both routes "www.yourdomain.com/" and "yourdomain.com/".

you can achieve the same result by having one single rule using an asterisk, like I mentioned in this past comment:

https://gist.github.com/mayneyao/b9fefc9625b76f70488e5d8c2a99315d#gistcomment-3095515

In my case, it didn't work out.
Only after I have separated into two routes, I could get rid of 520 Error. I'm not too sure why.

@edgarasben
Copy link

Yeah with one, did not work for me either, that's why I put two. I never have any other *.mycomain.com case anyways.

@edgarasben
Copy link

Hey, guys! I just moved my 2 domains to Porkbun and my site stopped working! Any ideas why? www.edgaras.com

@douglarek
Copy link

VM42 0d54f02a6f8941648c9bf2a6c5fd8ba0:64 Uncaught TypeError: replaceEle is not a function
    at VM42 0d54f02a6f8941648c9bf2a6c5fd8ba0:64
    at Array.forEach (<anonymous>)
    at VM42 0d54f02a6f8941648c9bf2a6c5fd8ba0:62
    at Array.forEach (<anonymous>)
    at MutationObserver.<anonymous> (VM42 0d54f02a6f8941648c9bf2a6c5fd8ba0:52)

@douglarek
Copy link

Please support disqus loading on demand, thanks

@naman1-gupta
Copy link

In case someone is facing 4xx errors on their images, you can try appending the query search params on the url on Line 69

Example:
Old

response = await fetch(`https://www.notion.so${url.pathname}`, {
      body: request.body, // must match 'Content-Type' header
      headers: request.headers,
      method: request.method, // *GET, POST, PUT, DELETE, etc.
    })

New

response = await fetch(`https://www.notion.so${url.pathname}${url.search}`, {
      body: request.body, // must match 'Content-Type' header
      headers: request.headers,
      method: request.method, // *GET, POST, PUT, DELETE, etc.
    })

@nathankg
Copy link

nathankg commented Sep 7, 2020

This was helpful, thanks @naman1-gupta

@stephenou
Copy link

An alternative is to set url.host = 'www.notion.so and use url.toString() for all the URLs. This guarantees not only search params but also fragments are included: https://github.com/stephenou/fruitionsite/blob/master/worker.js#L87

@yocoldle
Copy link

In case someone is facing 4xx errors on their images, you can try appending the query search params on the url on Line 69

Example:
Old

response = await fetch(`https://www.notion.so${url.pathname}`, {
      body: request.body, // must match 'Content-Type' header
      headers: request.headers,
      method: request.method, // *GET, POST, PUT, DELETE, etc.
    })

New

response = await fetch(`https://www.notion.so${url.pathname}${url.search}`, {
      body: request.body, // must match 'Content-Type' header
      headers: request.headers,
      method: request.method, // *GET, POST, PUT, DELETE, etc.
    })

This really solves the problem that pictures uploaded in notion cannot be opened, thx a lot!

@softmarshmallow
Copy link

Need working example for subdomain! getting DNS_PROBE_FINISHED_NXDOMAIN

@abhi5658
Copy link

abhi5658 commented Jan 23, 2021

If you are facing issues, you can refer this ->> https://fruitionsite.com/

@FranckNussbaumer
Copy link

Hello,
I am facing a problem.
I would like to redirect the domain I have acquired on google domains to a notion page.
I have set up the worker, a A record, and the DNS but somehow it does not redict it.

I have an : ERR_NAME_NOT_RESOLVED error

telling me : impossible to find IP adresse

Do you have any chere it can come from ?

Cheers,

@Valentyn-Kubrak
Copy link

In case someone is facing 4xx errors on their images, you can try appending the query search params on the url on Line 69

Example:
Old

response = await fetch(`https://www.notion.so${url.pathname}`, {
      body: request.body, // must match 'Content-Type' header
      headers: request.headers,
      method: request.method, // *GET, POST, PUT, DELETE, etc.
    })

New

response = await fetch(`https://www.notion.so${url.pathname}${url.search}`, {
      body: request.body, // must match 'Content-Type' header
      headers: request.headers,
      method: request.method, // *GET, POST, PUT, DELETE, etc.
    })

amazing. thanks

@RamScreen
Copy link

Thanks, perfect work!

@markliuyuxiang
Copy link

markliuyuxiang commented Feb 23, 2021

Try NotionDog if you have no custom domain and no money. 😉

I believe it's the easiest way to build simple, powerful websites with nothing but Notion.

https://github.com/notiondog/notion.dog

showcases:

https://amyrogers.notion.dog

https://aaron.notion.dog

https://virtualmojito.notion.dog

@Nhajela
Copy link

Nhajela commented Jul 27, 2021

Hey there, Really appreciate this script, I am struggling with changing it with how Notion has changed their public site link options. I've used the FruitionSite Script could anyone help with this?

@rakendd
Copy link

rakendd commented Aug 23, 2021

Notion errors out with "Mismatch between origin and baseUrl (dev)." during the worker preview. Any thoughts?

Generate again the worker script from the fruition site and paste into the Cloudflare worker script.
It worked for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment