Home

Awesome

Action-NotionSite

loconotion + Github Actions + Notion + GitHub Pages + Cloudflare Workers, Create a free website or build a blog.<br> GitHub 建 blog 的项目蛮多的,但是发布管理还是不够方便。Notion 本身是一款功能丰富的非常优秀的笔记软件,这个项目借助 Notion 搭建网站,做到了免费、快捷、方便、易用。一次部署再也不用管他。

English Version

Examples 示例站 loconotion-example.netlify.app

Usage

  1. 注册并在 Notion 创建你心仪的页面,点分享获得公开链接例如:https://www.notion.so/Loconotion-Example-Page-03c40xxxxxxxxxxxxxxxx9a8950ef
  2. 注册 GitHub, 点击本项目页面中的 use this template (使用这个模版)按钮,创建属于你的新项目。新仓库名字建议用 blog 之类。(创建完回来可以顺便给本项目点个 Star)
  3. 在你刚新建的 GitHub 项目里面点 Settings(设置)然后点 Secrets(隐私)新建配置文件。详细配置文件见原项目说明
<details> <summary>配置文件示例</summary>

Name:<br> SITE_CONFIG<br> Value:<br>

name = "notion"
page = "https://www.notion.so/Loconotion-Example-Page-03c40xxxxxxxxxxxxxxxx9a8950ef"
theme = "dark"
[site]

  [[site.meta]]
  name = "title"
  content = "Loconotion Test Site"

  [[site.meta]]
  name = "description"
  content = "A static site generated from a Notion.so page using Loconotion"

  [site.fonts]
  site = 'Nunito'
  navbar = ''
  title = 'Montserrat'
  h1 = 'Montserrat'
  h2 = 'Montserrat'
  h3 = 'Montserrat'
  body = ''
  code = ''

  [[site.inject.head.link]]
  rel="icon"
  sizes="16x16"
  type="image/png"
  href="/example/favicon-16x16.png"

  [[site.inject.body.script]]
  type="text/javascript"
  src="/example/custom-script.js"

[pages]

  [pages.d2fa06f244e64f66880bb0491f58223d]
    slug = "games-list"

    [[pages.d2fa06f244e64f66880bb0491f58223d.meta]]
    name = "description"
    content = "A fullscreen list database page, now with a pretty slug"

    [pages.d2fa06f244e64f66880bb0491f58223d.fonts]
    body = 'DM Mono'

  [pages.54dab6011e604430a21dc477cb8e4e3a]
    slug = "film-gallery"

  [pages.2604ce45890645c79f67d92833083fee]
    slug = "books-table"

  [pages.ae0a85c527824a3a855b7f4d31f4e0fc]
    slug = "random-board"
</details>
  1. 在你刚建的 GitHub 项目里点 Actions 然后点左侧 Deploy to Pages 切换,然后点 Run workflow 开始第一次运行生成 Pages (看不到 Auto Install 的话,点开 .yml 文件随便加一空行保存)。这里生成完了以后一定要先检查一下你这个项目下是否生成了新的分支 gh-pages ,看看该分支内是否有文件夹和 html 文件。
  2. 在你刚新建的 GitHub 项目里面点 Settings(设置)然后下拉,找到 GitHub Pages, 选择 gh-pages / root 保存后就能开启 GitHub Pages. 接下来就可以使用 username.github.io/repository/name 来访问你的静态网站了。这里的 username 是你的 GitHub 名字、repository 是你的项目名字、后面的 name 是配置文件第一行的名字,也对应 gh-pages 分支下的文件夹。
  3. 可选。注册并在 Cloudflare 上新增你的域名。新建 Workers, 使用下面的代码对 username.github.io/repository/name 进行反代。然后对应域名新建路由对应 Workers 即可实现域名访问。这一步网上很多教程。(如果你建立的是主 GitHub Pages 仓库<user>.github.io,则可以直接使用 GitHub Pages 自带的 Custom domain 来实现独立域名。GitHub 文档
<details> <summary>workers.js</summary>

from:booster.js

const config = {
  basic: {
    upstream: 'https://en.wikipedia.org/',
    mobileRedirect: 'https://en.m.wikipedia.org/',
  },

  firewall: {
    blockedRegion: ['CN', 'KP', 'SY', 'PK', 'CU'],
    blockedIPAddress: [],
    scrapeShield: true,
  },

  routes: {
    TW: 'https://zh.wikipedia.org/',
    HK: 'https://zh.wikipedia.org/',
    FR: 'https://fr.wikipedia.org/',
  },

  optimization: {
    cacheEverything: false,
    cacheTtl: 5,
    mirage: true,
    polish: 'off',
    minify: {
      javascript: true,
      css: true,
      html: true,
    },
  },
};

async function isMobile(userAgent) {
  const agents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod'];
  return agents.any((agent) => userAgent.indexOf(agent) > 0);
}

async function fetchAndApply(request) {
  const region = request.headers.get('cf-ipcountry') || '';
  const ipAddress = request.headers.get('cf-connecting-ip') || '';
  const userAgent = request.headers.get('user-agent') || '';

  if (region !== '' && config.firewall.blockedRegion.includes(region.toUpperCase())) {
    return new Response(
      'Access denied: booster.js is not available in your region.',
      {
        status: 403,
      },
    );
  } if (ipAddress !== '' && config.firewall.blockedIPAddress.includes(ipAddress)) {
    return new Response(
      'Access denied: Your IP address is blocked by booster.js.',
      {
        status: 403,
      },
    );
  }

  const requestURL = new URL(request.url);
  let upstreamURL = null;

  if (userAgent && isMobile(userAgent) === true) {
    upstreamURL = new URL(config.basic.mobileRedirect);
  } else if (region && region.toUpperCase() in config.routes) {
    upstreamURL = new URL(config.routes[region.toUpperCase()]);
  } else {
    upstreamURL = new URL(config.basic.upstream);
  }

  requestURL.protocol = upstreamURL.protocol;
  requestURL.host = upstreamURL.host;
  requestURL.pathname = upstreamURL.pathname + requestURL.pathname;

  let newRequest;
  if (request.method === 'GET' || request.method === 'HEAD') {
    newRequest = new Request(requestURL, {
      cf: {
        cacheEverything: config.optimization.cacheEverything,
        cacheTtl: config.optimization.cacheTtl,
        mirage: config.optimization.mirage,
        polish: config.optimization.polish,
        minify: config.optimization.minify,
        scrapeShield: config.firewall.scrapeShield,
      },
      method: request.method,
      headers: request.headers,
    });
  } else {
    const requestBody = await request.text();
    newRequest = new Request(requestURL, {
      cf: {
        cacheEverything: config.optimization.cacheEverything,
        cacheTtl: config.optimization.cacheTtl,
        mirage: config.optimization.mirage,
        polish: config.optimization.polish,
        minify: config.optimization.minify,
        scrapeShield: config.firewall.scrapeShield,
      },
      method: request.method,
      headers: request.headers,
      body: requestBody,
    });
  }

  const fetchedResponse = await fetch(newRequest);

  const modifiedResponseHeaders = new Headers(fetchedResponse.headers);
  if (modifiedResponseHeaders.has('x-pjax-url')) {
    const pjaxURL = new URL(modifiedResponseHeaders.get('x-pjax-url'));
    pjaxURL.protocol = requestURL.protocol;
    pjaxURL.host = requestURL.host;
    pjaxURL.pathname = pjaxURL.path.replace(requestURL.pathname, '/');

    modifiedResponseHeaders.set(
      'x-pjax-url',
      pjaxURL.href,
    );
  }

  return new Response(
    fetchedResponse.body,
    {
      headers: modifiedResponseHeaders,
      status: fetchedResponse.status,
      statusText: fetchedResponse.statusText,
    },
  );
}

// eslint-disable-next-line no-restricted-globals
addEventListener('fetch', (event) => {
  event.respondWith(fetchAndApply(event.request));
});
</details>

Tips

English Version

by TechCrunch

Usage

  1. Register and create a page of your choice in Notion, click share to get the public link e.g. https://www.notion.so/Loconotion-Example-Page-03c40xxxxxxxxxxxxxxxx9a8950ef
  2. To sign up for GitHub, click the use this template button on this project page to create your new project. We recommend using something like blog for the name of your new repository. (You can give this project a Star when you're done creating it)
  3. In the GitHub project you just created, click Settings and then** Secrets** to create a new profile. Detailed profile of the original project
<details> <summary>Example configuration file</summary>

Name:<br> SITE_CONFIG<br> Value:<br>

name = "notion"
page = "https://www.notion.so/Loconotion-Example-Page-03c40xxxxxxxxxxxxxxxx9a8950ef"
theme = "dark"
[site]

  [[site.meta]]
  name = "title"
  content = "Loconotion Test Site"

  [[site.meta]]
  name = "description"
  content = "A static site generated from a Notion.so page using Loconotion"

  [site.fonts]
  site = 'Nunito'
  navbar = ''
  title = 'Montserrat'
  h1 = 'Montserrat'
  h2 = 'Montserrat'
  h3 = 'Montserrat'
  body = ''
  code = ''

  [[site.inject.head.link]]
  rel="icon"
  sizes="16x16"
  type="image/png"
  href="/example/favicon-16x16.png"

  [[site.inject.body.script]]
  type="text/javascript"
  src="/example/custom-script.js"

[pages]

  [pages.d2fa06f244e64f66880bb0491f58223d]
    slug = "games-list"

    [[pages.d2fa06f244e64f66880bb0491f58223d.meta]]
    name = "description"
    content = "A fullscreen list database page, now with a pretty slug"

    [pages.d2fa06f244e64f66880bb0491f58223d.fonts]
    body = 'DM Mono'

  [pages.54dab6011e604430a21dc477cb8e4e3a]
    slug = "film-gallery"

  [pages.2604ce45890645c79f67d92833083fee]
    slug = "books-table"

  [pages.ae0a85c527824a3a855b7f4d31f4e0fc]
    slug = "random-board"
</details>
  1. Click Actions in the GitHub project you just created, then click the Deploy to Pages toggle on the left, and then click Run workflow to start generating Pages for the first time.(If you don't see Auto Install, click on the .yml file and add a blank line to save it). After generating here, make sure to check if a new branch gh-pages has been created under your project and see if there are folders and html files in that branch.
  2. In the GitHub project you just created, click Settings and scroll down to GitHub Pages and select gh-pages / root. Save it and you'll be able to start GitHub Pages. Next, you can use username.github.io/repository/name to access your static site. The username is your GitHub name, the repository is the name of your project, and the name on the first line of the configuration file corresponds to the folder under the gh-pages branch.
  3. Optional. Register and add your domain on Cloudflare. Create a new Worker, use the following code to reverse proxy username.github.io/repository/name Then create a new route corresponding to the domain name corresponding to Workers to achieve domain access. This step online many tutorials.(If you are setting up the main GitHub Pages repository <user>.github.io, you can directly use the Custom domain that comes with GitHub Pages to implement a separate domain. GitHub Docs)
<details> <summary>workers.js</summary>

from:booster.js

const config = {
  basic: {
    upstream: 'https://en.wikipedia.org/',
    mobileRedirect: 'https://en.m.wikipedia.org/',
  },

  firewall: {
    blockedRegion: ['CN', 'KP', 'SY', 'PK', 'CU'],
    blockedIPAddress: [],
    scrapeShield: true,
  },

  routes: {
    TW: 'https://zh.wikipedia.org/',
    HK: 'https://zh.wikipedia.org/',
    FR: 'https://fr.wikipedia.org/',
  },

  optimization: {
    cacheEverything: false,
    cacheTtl: 5,
    mirage: true,
    polish: 'off',
    minify: {
      javascript: true,
      css: true,
      html: true,
    },
  },
};

async function isMobile(userAgent) {
  const agents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod'];
  return agents.any((agent) => userAgent.indexOf(agent) > 0);
}

async function fetchAndApply(request) {
  const region = request.headers.get('cf-ipcountry') || '';
  const ipAddress = request.headers.get('cf-connecting-ip') || '';
  const userAgent = request.headers.get('user-agent') || '';

  if (region !== '' && config.firewall.blockedRegion.includes(region.toUpperCase())) {
    return new Response(
      'Access denied: booster.js is not available in your region.',
      {
        status: 403,
      },
    );
  } if (ipAddress !== '' && config.firewall.blockedIPAddress.includes(ipAddress)) {
    return new Response(
      'Access denied: Your IP address is blocked by booster.js.',
      {
        status: 403,
      },
    );
  }

  const requestURL = new URL(request.url);
  let upstreamURL = null;

  if (userAgent && isMobile(userAgent) === true) {
    upstreamURL = new URL(config.basic.mobileRedirect);
  } else if (region && region.toUpperCase() in config.routes) {
    upstreamURL = new URL(config.routes[region.toUpperCase()]);
  } else {
    upstreamURL = new URL(config.basic.upstream);
  }

  requestURL.protocol = upstreamURL.protocol;
  requestURL.host = upstreamURL.host;
  requestURL.pathname = upstreamURL.pathname + requestURL.pathname;

  let newRequest;
  if (request.method === 'GET' || request.method === 'HEAD') {
    newRequest = new Request(requestURL, {
      cf: {
        cacheEverything: config.optimization.cacheEverything,
        cacheTtl: config.optimization.cacheTtl,
        mirage: config.optimization.mirage,
        polish: config.optimization.polish,
        minify: config.optimization.minify,
        scrapeShield: config.firewall.scrapeShield,
      },
      method: request.method,
      headers: request.headers,
    });
  } else {
    const requestBody = await request.text();
    newRequest = new Request(requestURL, {
      cf: {
        cacheEverything: config.optimization.cacheEverything,
        cacheTtl: config.optimization.cacheTtl,
        mirage: config.optimization.mirage,
        polish: config.optimization.polish,
        minify: config.optimization.minify,
        scrapeShield: config.firewall.scrapeShield,
      },
      method: request.method,
      headers: request.headers,
      body: requestBody,
    });
  }

  const fetchedResponse = await fetch(newRequest);

  const modifiedResponseHeaders = new Headers(fetchedResponse.headers);
  if (modifiedResponseHeaders.has('x-pjax-url')) {
    const pjaxURL = new URL(modifiedResponseHeaders.get('x-pjax-url'));
    pjaxURL.protocol = requestURL.protocol;
    pjaxURL.host = requestURL.host;
    pjaxURL.pathname = pjaxURL.path.replace(requestURL.pathname, '/');

    modifiedResponseHeaders.set(
      'x-pjax-url',
      pjaxURL.href,
    );
  }

  return new Response(
    fetchedResponse.body,
    {
      headers: modifiedResponseHeaders,
      status: fetchedResponse.status,
      statusText: fetchedResponse.statusText,
    },
  );
}

// eslint-disable-next-line no-restricted-globals
addEventListener('fetch', (event) => {
  event.respondWith(fetchAndApply(event.request));
});
</details>

Tips

Acknowledgments

License

MIT © XIA