Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: izuolan/notionic
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: robinv8/blog
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Able to merge. These branches can be automatically merged.
  • 19 commits
  • 26 files changed
  • 1 contributor

Commits on Dec 16, 2023

  1. Copy the full SHA
    6b4207f View commit details

Commits on Jan 1, 2024

  1. chore: 配置博客

    robinv8 committed Jan 1, 2024
    Copy the full SHA
    71c7912 View commit details
  2. Copy the full SHA
    6624f64 View commit details
  3. Copy the full SHA
    4739b6d View commit details
  4. Copy the full SHA
    34d3fe1 View commit details
  5. Copy the full SHA
    dafbc48 View commit details
  6. Copy the full SHA
    cdcd601 View commit details
  7. Copy the full SHA
    a8e6eff View commit details
  8. Copy the full SHA
    2b8947a View commit details
  9. Add NOTION_SPACES_ID secret

    robinv8 committed Jan 1, 2024
    Copy the full SHA
    64630b1 View commit details
  10. Copy the full SHA
    e063bde View commit details
  11. Copy the full SHA
    03a448b View commit details

Commits on Jan 2, 2024

  1. Copy the full SHA
    2c78fa9 View commit details
  2. Copy the full SHA
    d7b3d53 View commit details
  3. Copy the full SHA
    c5e4c8c View commit details

Commits on Apr 1, 2024

  1. Copy the full SHA
    fe2e531 View commit details

Commits on Apr 2, 2024

  1. Update blog.config.js

    robinv8 authored Apr 2, 2024
    Copy the full SHA
    cf8310d View commit details

Commits on Dec 22, 2024

  1. Update

    robinv8 committed Dec 22, 2024
    Copy the full SHA
    0daa56b View commit details

Commits on Jan 2, 2025

  1. Update

    robinv8 committed Jan 2, 2025
    Copy the full SHA
    6491b57 View commit details
3 changes: 0 additions & 3 deletions .env.example

This file was deleted.

96 changes: 96 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Server image publish

on:
push:
branches: ["main"]
tags: ["v*.*.*"]
pull_request:
branches: ["main"]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write

steps:
- name: Checkout
uses: actions/checkout@v3


- name: Install Cosign
uses: sigstore/cosign-installer@v3.3.0
with:
cosign-release: 'v2.2.2'
- name: Check install!
run: cosign version

- name: Setup Docker buildx
uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf

- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16

- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install

- name: Build
run: pnpm run build
env:
NOTION_PAGE_ID: ${{ secrets.NOTION_PAGE_ID }}
NOTION_DOMAIN: ${{ secrets.NOTION_DOMAIN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
CRAFT_CONFIG_SHARE_URL: ${{ secrets.CRAFT_CONFIG_SHARE_URL }}
NOTION_SPACES_ID: ${{ secrets.NOTION_SPACES_ID }}

- name: Build and push
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
file: ./Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:cache
cache-to: type=inline
21 changes: 21 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM node:16-alpine
WORKDIR /app

ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1

COPY .next/standalone ./

COPY public ./public
COPY package.json ./package.json

COPY .next/static ./.next/static

EXPOSE 3000

ENV PORT 3000

# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD ["node", "server.js"]
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021-present, Zuo Lan
Copyright (c) 2021-present, Robin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
61 changes: 1 addition & 60 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,60 +1 @@
# Notionic

Notionic is a static blog that updates in real time, synchronizing changes to Notion pages without rebuilding the deployment.

![Notionic](./banner.png)

## Demo

- Notionic: [https://notionic.vercel.app](https://notionic.vercel.app)
- My Blog: [https://zuolan.me](https://zuolan.me)

## Features

- Incremental static regeneration
- Outline
- Theme switch
- Multi-language
- Native style comments
- Loading and transition animation
- Block page support
- SEO and Open Graph optimization
- Newsletter support
- Contact Form
- Telegram bot integration

## Quick Start

- Duplicate [Notionic template](https://izuolan.notion.site/87d5fa7c98e04cb79ef55f60989dc765), and share it to the public
- [Fork](https://github.com/izuolan/notionic/fork) this project
- **Customize `blog.config.js` file**
- _(Optional)_ Replace `favicon.svg/png/ico` in `public` folder with your own
- Modify `lib/lang.js` with your self introduction
- Deploy on [Vercel](https://vercel.com), set following environment variables:
- `NOTION_PAGE_ID` (Required): The ID of the Notion page you previously shared to the web, usually has 32 digits after your workspace address
- eg: `https://your-username.notion.site/<NOTION_PAGE_ID>?v=<view_id>`

More details about Notionic deployment:

- [English](https://zuolan.me/en/notionic_en)
- [中文](https://zuolan.me/notionic)

## Development

```bash
# Init
pnpm install
# Develop
./dev.sh
# Build & Serve
pnpm build
pnpm start
```

## Reference & License

- [Notion-X](https://github.com/NotionX/react-notion-x)
- [Nobelium](https://github.com/craigary/nobelium)
- [NotionNext](https://github.com/tangly1024/NotionNext)

The MIT License.
# 我的博客
38 changes: 19 additions & 19 deletions blog.config.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
const BLOG = {
title: 'Notionic',
author: '左蓝',
email: 'i@zuolan.me',
link: 'https://zuolan.me',
newsletter: 'Notionic Weekly',
description: 'A static blog build on top of Notion and Next.js',
lang: 'en-US', // ['en-US', 'zh-CN', 'zh-HK', 'zh-TW', 'ja-JP', 'es-ES']
title: 'robin 的博客',
author: 'robin',
email: 'robin@rnode.me',
link: 'https://blog.rnode.me',
newsletter: 'Weekly',
description: '记录生活,记录成长',
lang: 'zh-CN', // ['en-US', 'zh-CN', 'zh-HK', 'zh-TW', 'ja-JP', 'es-ES']
timezone: 'Asia/Shanghai', // See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for all options.
appearance: 'auto', // ['light', 'dark', 'auto'],
font: 'sans-serif', // ['sans-serif', 'serif']
lightBackground: '#F6F8FA', // use hex value, don't forget '#' e.g #fffefc
darkBackground: '#212936', // use hex value, don't forget '#'
path: '', // leave this empty unless you want to deploy Notionic in a folder
since: 2022, // If leave this empty, current year will be used.
since: 2018, // If leave this empty, current year will be used.
postsPerPage: 10,
sortByDate: true,
pagesShow: {
@@ -23,29 +23,29 @@ const BLOG = {
books: true,
friends: true
},
showWeChatPay: true,
previewImagesEnabled: true,
showWeChatPay: false,
previewImagesEnabled: false,
autoCollapsedNavBar: false, // The automatically collapsed navigation bar
ogImageGenerateHost: 'og-zl.vercel.app', // The link to generate OG image, don't end with a slash
defaultCover: '/cover.jpg',
socialLink: {
twitter: 'https://twitter.com/izuolan',
github: 'https://github.com/izuolan',
telegram: 'https://t.me/zuolan'
twitter: 'https://twitter.com/robinren716',
github: 'https://github.com/robinv8',
telegram: 'https://t.me/robin_0716'
},
seo: {
keywords: ['Notionic', 'Zuolan', 'Blog'],
keywords: ['robin', 'Blog'],
googleSiteVerification: '' // Remove the value or replace it with your own google site verification code
},
notionPageId: process.env.NOTION_PAGE_ID, // DO NOT CHANGE THIS! Edit .env file!
notionSpacesId: process.env.NOTION_SPACES_ID, // DO NOT CHANGE THIS! Edit .env file!
notionAccessToken: process.env.NOTION_ACCESS_TOKEN, // Useful if you prefer not to make your database public
notionDomain: 'izuolan.notion.site',
notionDomain: process.env.NOTION_DOMAIN,
telegramToken: process.env.TELEGRAM_TOKEN, // The token of your Telegram bot
telegramChatId: '263895784', // The chat id of your Telegram bot
telegramChannelUrl: 'https://channel.zuolan.me/', // The link of your Telegram channel
telegramChannelName: 'zuolan_me', // The name of your Telegram channel
craftConfigShareUrl: 'https://www.craft.do/s/kQtcWqkv98cHhB', // The link to share your craft config
telegramChatId: process.env.TELEGRAM_CHAT_ID, // The chat id of your Telegram bot
telegramChannelUrl: '', // The link of your Telegram channel
telegramChannelName: '', // The name of your Telegram channel
craftConfigShareUrl: process.env.CRAFT_CONFIG_SHARE_URL, // The link to share your craft config
analytics: {
provider: '', // Currently we support Google Analytics, Ackee, Umami and Cloudflare Insights, please fill with 'ga' or 'ackee' or 'umami' or 'cf', leave it empty to disable it.
ackeeConfig: {
7 changes: 1 addition & 6 deletions components/BlogPost.js
Original file line number Diff line number Diff line change
@@ -13,12 +13,7 @@ const BlogPost = ({ post }) => {
key={post.id}
className='group flex flex-col overflow-hidden relative mb-5 md:mb-8 cursor-pointer rounded-xl p-5'
>
<Image
fill
alt={`${post.title}`}
src={post?.page_cover}
className='w-full h-full object-cover object-center absolute inset-0 group-hover:scale-110 transition duration-200'
/>

<div className='hidden md:block md-cover absolute inset-0'></div>
<div className='md:hidden sm-cover absolute inset-0'></div>
<div className='relative mt-auto'>
23 changes: 10 additions & 13 deletions components/Common/Logo.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
// https://react-svgr.com/playground/
import * as React from 'react'
import Image from 'next/image'
import avatar from '@/public/avatar.webp'

const Logo = (props) => (
<svg
xmlns='http://www.w3.org/2000/svg'
width='24'
height='24'
viewBox='0 0 100 100'
<Image
{...props}
src={avatar}
style={{
opacity: 1
height: '3rem',
width: '3rem',
borderRadius: '50%'
}}
{...props}
>
<g transform='translate(0.000000,100) scale(0.080000,-0.080000)'>
<path d='M762 1203 c-6 -15 -13 -46 -17 -68 -4 -22 -13 -49 -20 -61 -15 -23 -122 -69 -257 -109 -49 -14 -88 -28 -88 -29 0 -2 33 -20 73 -40 49 -24 87 -36 115 -36 28 0 42 -4 42 -13 0 -34 -295 -517 -390 -639 -40 -52 -4 -28 86 56 49 46 105 109 124 141 19 31 64 98 100 148 77 108 125 186 173 283 20 39 46 78 59 86 13 8 69 34 126 58 107 45 118 57 110 111 -3 21 -10 25 -78 34 l-75 10 -5 45 c-5 42 -7 45 -36 48 -26 3 -33 -1 -42 -25z' />
<path d='M754 616 c-40 -19 -88 -39 -108 -46 -43 -14 -45 -30 -7 -72 25 -28 33 -31 80 -30 39 1 54 -3 58 -15 7 -18 -30 -140 -58 -192 -36 -67 6 -93 135 -84 l86 6 0 -26 c0 -14 -4 -37 -10 -51 -5 -14 -8 -26 -6 -26 7 0 110 68 129 85 11 10 17 30 17 60 0 62 -22 70 -150 57 -52 -5 -98 -6 -103 -2 -4 3 3 31 16 61 13 30 32 78 42 108 10 30 28 70 41 89 26 38 30 63 14 93 -17 31 -91 25 -176 -15z' />
</g>
</svg>
alt='logo'
/>
)

export default Logo
41 changes: 22 additions & 19 deletions components/Loading.js
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import { useRouter } from 'next/router'
import { useState } from 'react'
import { ExternalLinkIcon } from '@heroicons/react/outline'
import Link from 'next/link'
import Logo from '@/components/Common/Logo'

export default function Loading({ notionSlug }) {
const { locale } = useRouter()
@@ -20,38 +21,40 @@ export default function Loading({ notionSlug }) {
<div className='py-6 sm:py-8 lg:py-12'>
<div className='max-w-screen-2xl px-4 md:px-8 mx-auto'>
<div className='flex flex-col items-center'>
<div className='inline-flex items-center gap-2.5 mb-8'>
<p className='inline-flex items-center text-sm md:text-base font-semibold uppercase mb-4'>
<svg
className='animate-spin -ml-1 mr-3 h-5 w-5 text-gray-400'
xmlns='http://www.w3.org/2000/svg'
width='24'
height='24'
viewBox='0 0 100 100'
className='h-6 hover:text-blue-500 fill-current dark:text-white'
fill='none'
viewBox='0 0 24 24'
>
<g transform='translate(0.000000,100) scale(0.080000,-0.080000)'>
<path d='M762 1203 c-6 -15 -13 -46 -17 -68 -4 -22 -13 -49 -20 -61 -15 -23 -122 -69 -257 -109 -49 -14 -88 -28 -88 -29 0 -2 33 -20 73 -40 49 -24 87 -36 115 -36 28 0 42 -4 42 -13 0 -34 -295 -517 -390 -639 -40 -52 -4 -28 86 56 49 46 105 109 124 141 19 31 64 98 100 148 77 108 125 186 173 283 20 39 46 78 59 86 13 8 69 34 126 58 107 45 118 57 110 111 -3 21 -10 25 -78 34 l-75 10 -5 45 c-5 42 -7 45 -36 48 -26 3 -33 -1 -42 -25z' />
<path d='M754 616 c-40 -19 -88 -39 -108 -46 -43 -14 -45 -30 -7 -72 25 -28 33 -31 80 -30 39 1 54 -3 58 -15 7 -18 -30 -140 -58 -192 -36 -67 6 -93 135 -84 l86 6 0 -26 c0 -14 -4 -37 -10 -51 -5 -14 -8 -26 -6 -26 7 0 110 68 129 85 11 10 17 30 17 60 0 62 -22 70 -150 57 -52 -5 -98 -6 -103 -2 -4 3 3 31 16 61 13 30 32 78 42 108 10 30 28 70 41 89 26 38 30 63 14 93 -17 31 -91 25 -176 -15z' />
</g>
</svg>
</div>

<p className='inline-flex items-center text-sm md:text-base font-semibold uppercase mb-4'>
<svg className='animate-spin -ml-1 mr-3 h-5 w-5 text-gray-400' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
<circle className='opacity-25' cx='12' cy='12' r='10' stroke='currentColor' strokeWidth='4'></circle>
<path className='opacity-75' fill='currentColor' d='M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'></path>
<circle
className='opacity-25'
cx='12'
cy='12'
r='10'
stroke='currentColor'
strokeWidth='4'
></circle>
<path
className='opacity-75'
fill='currentColor'
d='M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'
></path>
</svg>
{t.ERROR.LOADING}
</p>
{showNotion &&
{showNotion && (
<Link
passHref
href={`https://${BLOG.notionDomain}/${notionSlug}`} scroll={false}
href={`https://${BLOG.notionDomain}/${notionSlug}`}
scroll={false}
className='text-gray-500 hover:text-gray-600 dark:text-gray-400 dark:hover:text-gray-300 transition duration-100'
>
<ExternalLinkIcon className='inline-block mb-1 h-5 w-5' />
<span className='m-1'>{t.ERROR.TIMEOUT_TEXT}</span>
</Link>
}
)}
</div>
</div>
</div>
2 changes: 1 addition & 1 deletion lib/getBlocksMaps.js
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ import BLOG from '@/blog.config'

// 从 Config 页面的 API 获取两个表格的内容, 并处理成两个 json 返回给 htmlrewrite.js
export async function getBlocksMaps() {
const craftConfigSecret = BLOG.craftConfigShareUrl.slice(23)
const craftConfigSecret = BLOG.craftConfigShareUrl.split('/').pop()
const craftConfigApiUrl = 'https://www.craft.do/api/share/' + craftConfigSecret
const init = {
headers: {
Loading