Skip to content

chenzihui222/Reading-Notes

Repository files navigation

Zihui Notes

私人快速笔记工具 —— 随时随地记录想法、灵感和学习笔记。

线上地址: https://notes.zihuichen.com


目录


这个项目是什么?

Zihui Notes 是一个私人快速笔记工具。核心理念是 方便 —— 打开网页就能写,写完即保存。

功能:

  • 快速记录:一个文本框,写完按 ⌘+Enter 即可保存
  • 引用来源:每条笔记可以附带一个引用(URL 或文字),URL 自动变成可点击的链接
  • 点赞互动:登录用户可以对笔记点赞,显示点赞者的 GitHub 头像
  • GitHub 登录:通过 GitHub OAuth 认证身份
  • 管理功能:管理员可以创建和删除笔记

技术栈总览

技术 作用
框架 vinext (Next.js 16 on Vite) 用 Next.js 的写法(App Router、Server Components、API Routes),底层用 Vite 打包,部署到 Cloudflare
前端 React 19 页面交互(笔记创建、点赞、删除)
运行环境 Cloudflare Workers 代码运行在 Cloudflare 全球边缘节点
数据库 Cloudflare D1 (SQLite) 存储笔记、点赞记录、用户会话
认证 GitHub OAuth 用户登录和身份验证
构建工具 Vite 7 + TypeScript 代码打包和类型检查
域名 notes.zihuichen.com 通过 Cloudflare Workers Custom Domain 绑定

架构图

用户浏览器
    |
    | 访问 notes.zihuichen.com
    v
┌─────────────────────────────────────────────────────┐
│              Cloudflare Workers (边缘节点)             │
│                                                      │
│  ┌────────────┐  ┌──────────────┐  ┌─────────────┐  │
│  │   vinext    │  │  API Routes  │  │  GitHub      │  │
│  │   (SSR)     │  │              │  │  OAuth       │  │
│  │            │  │ /api/notes   │  │              │  │
│  │ 服务端渲染  │  │ /api/notes/  │  │ /api/auth/   │  │
│  │ 首页 HTML   │  │  [id]/like   │  │ login        │  │
│  └─────┬──────┘  └─────┬──────┘  │ callback     │  │
│        │               │          │ logout       │  │
│        │               │          └──────┬──────┘  │
│        └───────────────┴─────────────────┘          │
│                        │                             │
│              ┌─────────┴─────────┐                   │
│              │        D1         │                   │
│              │     (SQLite)      │                   │
│              │                   │                   │
│              │  notes 表         │                   │
│              │  likes 表         │                   │
│              │  sessions 表      │                   │
│              └───────────────────┘                   │
└─────────────────────────────────────────────────────┘
         │
         │ OAuth 回调
         v
    GitHub API
    (用户身份验证)

简单来说:

  1. 用户访问网站,vinext 在服务端从 D1 读取笔记和点赞数据,渲染成 HTML 返回
  2. 浏览器拿到页面后,React 接管交互(写笔记、点赞、删除)
  3. 交互操作通过 API Routes 与 D1 数据库通信

项目结构详解

zihui_notes/
├── app/                          # 前端页面和 API
│   ├── layout.tsx                # 根布局 —— HTML 外壳、字体加载
│   ├── page.tsx                  # 首页 —— 服务端组件,从 D1 读数据
│   ├── home.tsx                  # 客户端交互组件 —— 笔记创建、点赞、背景效果
│   ├── globals.css               # 全局样式(网格纸主题、动画、响应式)
│   └── api/
│       ├── notes/
│       │   ├── route.ts          # GET /api/notes(获取所有笔记)
│       │   │                       POST /api/notes(创建笔记,需管理员权限)
│       │   └── [id]/
│       │       ├── route.ts      # DELETE /api/notes/:id(删除笔记,需管理员权限)
│       │       └── like/
│       │           └── route.ts  # POST /api/notes/:id/like(点赞/取消点赞,需登录)
│       └── auth/
│           ├── login/route.ts    # GET  —— 重定向到 GitHub OAuth 授权页
│           ├── callback/route.ts # GET  —— GitHub 回调,交换 token、创建会话
│           └── logout/route.ts   # POST —— 清除会话 cookie
├── lib/                          # 后端业务逻辑
│   ├── db.ts                     # 数据库操作(笔记 CRUD、点赞、会话管理)
│   ├── auth.ts                   # 认证逻辑(session cookie、用户身份、管理员判断)
│   └── env.ts                    # Cloudflare 环境变量获取
├── worker/                       # Cloudflare Workers 入口
│   └── index.ts                  # HTTP 请求入口(vinext 自动生成)
├── migrations/                   # 数据库迁移文件(按顺序执行)
│   ├── 0001_init.sql             # 初始建表(旧版书评结构,已被 0003 替代)
│   ├── 0003_simplify.sql         # 简化为快速笔记结构
│   └── 0004_likes.sql            # 添加点赞表
├── vite.config.ts                # Vite 构建配置
├── wrangler.jsonc                # Cloudflare Workers 配置
├── tsconfig.json                 # TypeScript 配置
└── package.json                  # 依赖和脚本命令

app/ — 前端页面和 API

app/layout.tsx — 根布局

所有页面共享的 HTML 外壳。定义了:

  • <html lang="zh-CN"> —— 中文页面
  • Google Fonts 加载:Bricolage Grotesque(标题/正文)和 JetBrains Mono(等宽字体,用于时间戳、引用等)
  • 引入全局样式 globals.css
  • 页面 metadata(标题和描述)

app/page.tsx — 首页(服务端组件)

服务端组件,在 Workers 上运行。做两件事:

  1. 从请求的 Cookie 中识别当前用户身份
  2. 从 D1 查询所有笔记(含点赞数据)

然后把 notesuser 作为 props 传给客户端组件 <HomePage>

用户请求 → 服务端执行 page.tsx → 查询 D1 → 渲染 HTML → 返回浏览器

app/home.tsx — 客户端交互组件

文件顶部的 "use client" 标记,表示这个组件在浏览器运行。包含以下子组件:

组件 功能
QuickCapture 笔记输入框 + 引用输入 + ⌘+Enter 快捷保存
NoteItem 单条笔记的展示:内容、引用链接、时间、删除按钮
LikeButton 点赞按钮 + 点赞者头像展示,支持乐观更新
SiteFooter 页脚:项目链接、GitHub、版权信息
useGridBackground() Canvas 网格纸背景动画 + 鼠标交互 ripple 效果

背景效果:使用 Canvas 在淡黄色底色上绘制网格线和网格交叉点。鼠标移动时,附近的网格点会被推开并变色(灰→橙),点之间会出现橙色连接线,鼠标离开后弹簧回弹。适配了 devicePixelRatio 以在 Retina 屏幕上保持清晰。

app/globals.css — 全局样式

设计系统采用 CSS 变量,主要特征:

  • 淡黄色网格纸背景(--bg: #fdf8ee
  • 橙色作为强调色(--accent: #f38020
  • 半透明白色卡片(--card: rgba(255,255,255,0.85)
  • 入场动画(fadeUp + 交错延迟)
  • 点赞按钮弹跳动画(likePop
  • 移动端响应式适配(640px 断点)

API Routes

路由 方法 功能 权限
/api/notes GET 获取所有笔记(含点赞) 公开
/api/notes POST 创建新笔记 管理员
/api/notes/:id DELETE 删除笔记 管理员
/api/notes/:id/like POST 点赞/取消点赞(toggle) 登录用户
/api/auth/login GET 跳转 GitHub 授权页 公开
/api/auth/callback GET GitHub OAuth 回调 公开
/api/auth/logout POST 退出登录 公开

lib/ — 后端逻辑

lib/db.ts — 数据库操作

所有 D1 数据库交互封装在这个文件中:

函数 作用
getNotes() 查询所有笔记,并批量查询每条笔记的点赞列表,合并返回
createNote() 插入新笔记,返回自增 ID
deleteNote() 删除笔记及其关联的点赞记录
toggleLike() 切换点赞状态:已赞则取消,未赞则添加
createSession() 创建登录会话
getSession() 根据 session ID 查询会话(自动过滤已过期的)
deleteSession() 删除会话(退出登录时调用)

getNotes() 的实现细节:先查 notes 表,再用 WHERE note_id IN (...) 批量查 likes 表,在内存中合并。避免了 N+1 查询问题。

lib/auth.ts — 认证逻辑

  • 从请求的 Cookie 中提取 session ID
  • 查询 D1 中的 sessions 表验证身份
  • 判断是否为管理员(对比 GitHub 用户名列表)
  • Session cookie 设置:HttpOnly; Secure; SameSite=Lax; Max-Age=604800(7 天过期)

lib/env.ts — 环境变量

封装 Cloudflare Workers 的环境变量获取。在 Workers 中,环境变量不通过 process.env 获取,而是通过 cloudflare:workers 模块。

提供 DB(D1 数据库)、GITHUB_CLIENT_IDGITHUB_CLIENT_SECRET 三个绑定。


worker/ — Cloudflare Workers 入口

worker/index.ts

由 vinext 框架自动生成的 Workers 入口文件。每当 HTTP 请求到达时,Workers 调用此文件的 fetch() 函数,由 vinext 路由到对应的页面或 API。


migrations/ — 数据库迁移

按编号顺序执行的 SQL 文件,用于版本控制数据库结构。

文件 内容 说明
0001_init.sql 创建旧版表(notes、recommendations、sessions) 初始版本,已被 0003 替代
0003_simplify.sql 删除旧表,创建新的简化 notes 表 从书评模式转为快速笔记模式
0004_likes.sql 创建 likes 表 添加点赞功能

注意:编号 0002 是历史遗留的 seed 数据文件,已删除。


配置文件

wrangler.jsonc — Cloudflare Workers 配置

{
  "name": "zihui-notes",                // Workers 项目名
  "main": "worker/index.ts",            // 入口文件
  "compatibility_date": "2026-03-01",    // Workers 运行时版本
  "compatibility_flags": ["nodejs_compat"], // Node.js 兼容模式
  "workers_dev": true,                   // 同时启用 workers.dev 域名
  "d1_databases": [{
    "binding": "DB",                     // 代码中用 env.DB 访问
    "database_name": "zihui-notes-db",
    "database_id": "698723a3-...",
    "migrations_dir": "migrations"
  }],
  "vars": {
    "GITHUB_CLIENT_ID": "Ov23li..."      // GitHub OAuth App 的 Client ID
  },
  "routes": [
    { "pattern": "notes.zihuichen.com", "custom_domain": true }
  ],
  "r2_buckets": [{                       // R2 存储桶(历史遗留,暂未使用)
    "binding": "IMAGES",
    "bucket_name": "zihui-notes-images"
  }]
}

GITHUB_CLIENT_SECRET 通过 wrangler secret 设置,不会出现在配置文件中。

vite.config.ts — Vite 构建配置

plugins: [
  vinext(),       // vinext 框架插件(处理 App Router、SSR、RSC)
  cloudflare()    // Cloudflare 插件(打包为 Workers 格式)
]

tsconfig.json — TypeScript 配置

  • "types": ["@cloudflare/workers-types/experimental"]:提供 Workers API 类型
  • "paths": { "@/*": ["./*"] }:路径别名,@/lib/db 解析为 ./lib/db

数据库设计

当前表结构

项目使用 Cloudflare D1(托管 SQLite),包含 3 张表:

notes 表 — 笔记

CREATE TABLE notes (
  id          INTEGER PRIMARY KEY AUTOINCREMENT,
  content     TEXT NOT NULL,          -- 笔记内容
  reference   TEXT DEFAULT '',        -- 引用来源(URL 或文字)
  created_at  TEXT DEFAULT (datetime('now'))  -- 创建时间(UTC)
);
CREATE INDEX idx_notes_created ON notes(created_at DESC);

likes 表 — 点赞

CREATE TABLE likes (
  note_id     INTEGER NOT NULL,       -- 关联的笔记 ID
  github_id   INTEGER NOT NULL,       -- 点赞者的 GitHub 用户 ID
  github_login TEXT NOT NULL,         -- 点赞者的 GitHub 用户名(用于显示头像)
  created_at  TEXT DEFAULT (datetime('now')),
  PRIMARY KEY (note_id, github_id)    -- 联合主键,防止重复点赞
);

点赞者头像通过 https://avatars.githubusercontent.com/u/{github_id}?s=32 直接从 GitHub 获取,无需额外存储。

sessions 表 — 登录会话

CREATE TABLE sessions (
  id          TEXT PRIMARY KEY,       -- 随机生成的 session ID(64 字符十六进制)
  github_id   INTEGER NOT NULL,       -- GitHub 用户 ID
  github_login TEXT NOT NULL,         -- GitHub 用户名
  created_at  TEXT DEFAULT (datetime('now')),
  expires_at  TEXT NOT NULL           -- 过期时间(创建后 7 天)
);

迁移历史

数据库经历了一次重大重构:

  1. 0001_init.sql(初始版本):创建了 notes(书评)、recommendations(推荐书目)、sessions 三张表。notes 表包含 title、author、content、cover_url 等书评相关字段。

  2. 0003_simplify.sql(简化重构):删除了 notesrecommendations 表,重建了全新的 notes 表,只保留 content(笔记内容)和 reference(引用来源)两个核心字段。这次迁移标志着项目从"读书笔记/书评网站"转型为"快速笔记工具"。

  3. 0004_likes.sql(添加点赞):创建 likes 表,支持登录用户对笔记点赞。

执行迁移的方式

# 本地
wrangler d1 execute zihui-notes-db --local --file=migrations/0004_likes.sql

# 线上
wrangler d1 execute zihui-notes-db --remote --file=migrations/0004_likes.sql

认证与权限

GitHub OAuth 流程

1. 用户点击 "Sign in"
       |
2. 重定向到 GitHub 授权页
   GET https://github.com/login/oauth/authorize
       ?client_id=xxx
       &redirect_uri=https://notes.zihuichen.com/api/auth/callback
       &scope=read:user
       |
3. 用户在 GitHub 授权后,GitHub 重定向回
   GET /api/auth/callback?code=xxx
       |
4. 服务端用 code 向 GitHub 交换 access_token
   POST https://github.com/login/oauth/access_token
       |
5. 用 access_token 调用 GitHub API 获取用户信息
   GET https://api.github.com/user
       |
6. 创建 session 存入 D1,设置 HttpOnly cookie
       |
7. 重定向回首页,用户已登录

权限模型

角色 能做什么
访客(未登录) 浏览所有笔记
登录用户 浏览笔记 + 点赞/取消点赞
管理员 浏览笔记 + 点赞 + 创建笔记 + 删除笔记

管理员身份在 lib/auth.ts 中通过 GitHub 用户名判断。


数据流:一条笔记从创建到显示的完整路径

1. [管理员] 在 QuickCapture 文本框中输入内容,按 ⌘+Enter
       |
2. [home.tsx] 调用 fetch("/api/notes", { method: "POST", body: { content, reference } })
       |
3. [api/notes/route.ts] 验证管理员身份 → 调用 createNote()
       |
4. [lib/db.ts] INSERT INTO notes (content, reference) VALUES (?, ?)
       |
5. [home.tsx] 保存成功后,调用 refresh() → fetch("/api/notes")
       |
6. [api/notes/route.ts] 调用 getNotes()
       |
7. [lib/db.ts] SELECT * FROM notes + SELECT * FROM likes WHERE note_id IN (...)
       |
8. [home.tsx] setNotes(data.notes) → React 重新渲染列表
       |
9. [浏览器] 用户看到新笔记出现在列表顶部

前端设计细节

网格纸背景

页面背景模拟淡黄色网格纸效果:

  • 底色 #fdf8ee(暖黄色)
  • Canvas 绘制棕色细网格线(间距 28px,透明度 0.28)
  • 网格交叉点有小圆点

鼠标 Ripple 交互

基于 Canvas 的实时动画(参考自 zihuichen.com 首页的点阵效果):

  • 鼠标靠近时,网格点被推开(弹簧物理模拟)
  • 点的颜色从灰色渐变为橙色
  • 附近的点之间出现橙色连接线
  • 鼠标离开后,点弹回原位
  • 适配 devicePixelRatio 保证 Retina 屏清晰

点赞交互

  • 点击心形按钮 toggle 点赞状态
  • 乐观更新:点击后立即更新 UI,不等服务端返回
  • 弹跳动画(likePop,300ms)
  • 点赞者的 GitHub 头像叠加排列显示

本地开发指南

前置条件

  • Node.js 18+
  • npm

首次设置

# 1. 安装依赖
npm install

# 2. 建立本地数据库
wrangler d1 execute zihui-notes-db --local --file=migrations/0001_init.sql
wrangler d1 execute zihui-notes-db --local --file=migrations/0003_simplify.sql
wrangler d1 execute zihui-notes-db --local --file=migrations/0004_likes.sql

启动开发服务器

注意:本地开发时需要临时修改配置。

编辑 vite.config.ts,去掉 Cloudflare 插件:

import vinext from "vinext";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [vinext()],
});

编辑 lib/env.ts,使用本地数据库:

import type { D1Database } from "@cloudflare/workers-types";

interface AppEnv { DB: D1Database; GITHUB_CLIENT_ID: string; GITHUB_CLIENT_SECRET: string; }

export async function getEnv(): Promise<AppEnv> {
  const { getPlatformProxy } = await import("wrangler");
  const proxy = await getPlatformProxy<AppEnv>();
  return proxy.env;
}

然后启动:

npx vinext dev --port 3002

打开 http://localhost:3002 即可看到网站。

重要:部署前记得把这两个文件改回生产版本。


部署到 Cloudflare

前置条件

  1. Cloudflare 账号
  2. 登录 wrangler:wrangler login

首次部署

# 1. 创建线上 D1 数据库(只需一次)
wrangler d1 create zihui-notes-db
# 记下返回的 database_id,填入 wrangler.jsonc

# 2. 在线上数据库执行迁移
wrangler d1 execute zihui-notes-db --remote --file=migrations/0001_init.sql
wrangler d1 execute zihui-notes-db --remote --file=migrations/0003_simplify.sql
wrangler d1 execute zihui-notes-db --remote --file=migrations/0004_likes.sql

# 3. 设置 GitHub OAuth Secret
npx wrangler secret put GITHUB_CLIENT_SECRET

# 4. 构建并部署
npx vinext build && npx wrangler deploy

后续更新

npx vinext build && npx wrangler deploy

GitHub OAuth 设置

  1. 前往 GitHub Developer Settings 创建 OAuth App
  2. Homepage URL: https://notes.zihuichen.com
  3. Authorization callback URL: https://notes.zihuichen.com/api/auth/callback
  4. 将 Client ID 填入 wrangler.jsoncGITHUB_CLIENT_ID
  5. 将 Client Secret 通过 npx wrangler secret put GITHUB_CLIENT_SECRET 设置

自定义域名

wrangler.jsonc 中配置 routes

"routes": [
  { "pattern": "notes.zihuichen.com", "custom_domain": true }
]

部署后 Cloudflare 会自动创建 DNS 记录和 SSL 证书。同时设置 "workers_dev": true 以保留 *.workers.dev 域名作为备用。


常用命令速查

命令 作用
npx vinext dev --port 3002 启动本地开发服务器
npx vinext build 构建生产版本
npx wrangler deploy 部署到 Cloudflare Workers
wrangler d1 execute zihui-notes-db --local --file=<sql> 在本地数据库执行 SQL 文件
wrangler d1 execute zihui-notes-db --remote --file=<sql> 在线上数据库执行 SQL 文件
wrangler d1 execute zihui-notes-db --local --command "SQL" 在本地执行单条 SQL
wrangler d1 execute zihui-notes-db --remote --command "SQL" 在线上执行单条 SQL
npx wrangler secret put GITHUB_CLIENT_SECRET 设置 OAuth Secret
wrangler tail 查看线上实时日志
wrangler login 登录 Cloudflare

Product By Chen Zihui

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors