Notion 页面公开以及分享

本篇将介绍如何将 Notion 页面公开以及分享,在进阶里会介绍配合 Cloudflare 将已公开的 Notion 页面换上个人域名。

Notion 的介绍

Notion 是一個將筆記、任務管理和知識庫相結合的跨平台協作工具,並具有 Markdown 支持,也可以通過大多數瀏覽器進行訪問。

https://zh.wikipedia.org/zh-tw/Notion?oldformat=true
Notion 是一個將筆記、任務管理和知識庫相結合的跨平台協作工具,並具有 Markdown 支持。

网站https://www.notion.so/

Notion 虽然有各个平台的 APP,但是与 Evernote 以及 OneNote 不同的地方是 Notion 没有离线功能,也就是因为这样几乎所有功能都必须在有网路的环境进行,这对部分人来说也许是缺点,也因为一开始是朝着云端协作工具的角度开发,不管用户是在手机端或是在游览器的方式使用的都能有统一的体验。

那么直接进入主题

如何将 Notion 页面公开分享呢?

基础

只要在到你所准备好的 Notion 文件里,选择分享 Share,就能看到如下图一样选择开启 Public Access,在这里的示范里我选择公开 Everblogger 的看板范本。


进阶

Notion 官方暂时还没提供 custom domain 的服务,所以现在只能搭配 Cloudflare 的 Workflow 进行。要注意的是 Cloudflare 免费用户在使用次数上有限制。

打个比方,我想自订到 notion.everblogger.cc,我就需要到 cloudflare 里配合的配合,相信不只是 Notion,其他网站也能应用类似的方法。

首先使用 Cloudflare 托管 Domain,这里 DNS

Cloudfalre DNS

这里的示范是notion.everblogger.cc,用 CNAME

Cloudfalre CNAME

接着到 Workers

Cloudfalre Workers

在选择 Lauch Editor,接着在 Add script 里粘贴下列的程式码

Cloudfalre Add script

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
  }
}
view raw
就像我将一下更改
const MY_DOMAIN = "notion.everblogger.cc"
const START_PAGE = "https://www.notion.so/366ac8b8488e43dcb2c4fe1cdfed079d?v=73b58e7553f24adfbd0746030407684a"
const DISQUS_SHORTNAME = "notion" 

最后在 Add route

Cloudfalre Add route
这里要注意的是在 Route 里的 URLs 后输入 /*

欢迎来到 everbogger 的 notion 看板!

Notion Board
https://www.notion.so/366ac8b8488e43dcb2c4fe1cdfed079d?v=73b58e7553f24adfbd0746030407684a
来源:https://medium.com/@TarasPyoneer/how-to-set-up-a-custom-domain-for-your-homepage-in-notion-53fa3d54f848

感想

接触了 Notion 的 Block 编辑器,觉得与 WordPress 后台的 Block 编辑器相比之下并没有多大的不同,甚至有过之而无不及。使用着 WordPress 的朋友相信不会对 Notion 的使用逻辑一头雾水,真希望下个版本的 WordPress 能吸收 Notion 的优点,尤其是 Code,以及网站链接的书签方式。

对 WordPress 有兴趣?物色好用的 WordPress 主机?这里推荐SiteGroud,一键设置,省时省力。

对微软产品有兴趣也能到微软官方商城

Default image
yong13579