-
Notifications
You must be signed in to change notification settings - Fork 38
feat: 新增仿notion设计的markdown编辑器 #230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
longsizhuo
merged 34 commits into
InvolutionHell:main
from
Crokily:feat/Crokily/mdEditor
Dec 13, 2025
Merged
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
6e7536d
chore(deps): add @milkdown/crepe and update related dependencies in p…
Crokily 065bbf3
Test selective commit with improved pre-commit hook
Crokily 9060f48
Fix: 修复了回调参数的error,目前未登录的页面能有可回调的登录页了
Crokily 875dd76
Feat: 第一阶段的编辑器组件
Crokily 4d682bb
feat: enhance EditorMetadataForm and MarkdownEditor with improved inp…
Crokily 037f9c0
chore: update .gitignore to include .claude and enhance pre-commit ho…
Crokily bfa8af3
Fix: 修复了标签不能正常分割的问题
Crokily ef0f53f
chore: update .gitignore to change Agents.md to AGENTS.md for consist…
Crokily 99f07f9
Fix: 修复了粘贴图片逻辑没走blob的问题
Crokily 01f4468
chore: add R2 storage configuration to .env.sample for image upload s…
Crokily e093559
chore(deps): add AWS SDK S3 client and request presigner to package.j…
Crokily 1509e2d
feat: implement image upload functionality to Cloudflare R2 with pre-…
Crokily 871d832
feat: 对 MarkdownEditor 进行了解耦重构,并修复了删除同步问题
Crokily 2a52b07
refactor: 抽离投稿目录与文件名工具
Crokily b56effe
feat: 编辑器接入 GitHub 投稿流程
Crokily d13d9ea
test: 添加一个编辑器生成的测试稿件
Crokily 15c63ee
chore(deps): add @milkdown/kit dependency to package.json and update …
Crokily c032a48
chore(deps): add @types/mdast and unist-util-visit dependencies to pa…
Crokily 5f07377
refactor: 移动 remarkImage 配置至 source.config.ts,并禁用 Next.js 图片优化以应对 Ver…
Crokily eafc34b
refactor: 使用原生 <img> 标签替代 Next.js Image 组件以解决 Vercel 配额限制和运行时问题
Crokily efb7d77
fix: correct JavaScript syntax highlighting in documentation
Crokily 503b03b
refactor: improve tag input handling in EditorMetadataForm for better…
Crokily eb0adbd
chore: remove deprecated test markdown file from agents-todo document…
Crokily 6b21a3c
feat: add navigation to editor in Contribute component
Crokily d33dbbf
Merge branch 'main' into feat/Crokily/mdEditor
Crokily f44f5df
chore(deps): update pnpm-lock.yaml to standardize package names and v…
Crokily 15e1988
refactor: remove eslint directive for <img> tag in MDX components to …
Crokily 66bfa55
fix: change overflow property in MarkdownEditor component to improve …
Crokily e00ceaf
fix: 修改 upload api 的注释以符合 API COMMENT 的规范
Crokily d8ac95b
Merge branch 'main' into feat/Crokily/mdEditor
Crokily 4508c2d
feat: 创建sanitizer.ts内有2种模式的文件名处理函数
Crokily dafa97f
feat: 文件名的处理代码统一使用宽松模式函数,图片等则使用严格清理
Crokily 001b99d
fix: 修复了copilot提出的一个报错处理不完善的问题
Crokily f60a363
fix: 还原预提交脚本
Crokily File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| import { auth } from "@/auth"; | ||
| import { NextRequest, NextResponse } from "next/server"; | ||
| import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; | ||
| import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; | ||
| import { sanitizeDocumentSlug, sanitizeResourceKey } from "@/lib/sanitizer"; | ||
|
|
||
| /** | ||
| * R2 配置 | ||
| * Cloudflare R2 兼容 S3 API,使用 AWS SDK 连接 | ||
| */ | ||
| const r2Client = new S3Client({ | ||
| region: "auto", | ||
| endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, | ||
| credentials: { | ||
| accessKeyId: process.env.R2_ACCESS_KEY_ID!, | ||
| secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!, | ||
| }, | ||
| }); | ||
|
|
||
| interface UploadRequest { | ||
| filename: string; | ||
| contentType: string; | ||
| articleSlug: string; | ||
| } | ||
|
|
||
| /** | ||
| * @description POST /api/upload - 生成 R2 预签名 URL,用于客户端直接上传图片 | ||
| * @param request - NextRequest 对象,请求体包含以下字段: | ||
| * - filename: 文件名 | ||
| * - contentType: 文件 MIME 类型 | ||
| * - articleSlug: 文章 slug(用于组织文件路径) | ||
| * @returns NextResponse - 返回 JSON 对象: | ||
| * - uploadUrl: 预签名上传 URL(用于 PUT 请求) | ||
| * - publicUrl: 图片的公开访问 URL | ||
| * - key: R2 对象键 | ||
| */ | ||
Crokily marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| export async function POST(request: NextRequest) { | ||
| try { | ||
| // 验证用户身份 | ||
| const session = await auth(); | ||
|
|
||
| if (!session?.user?.id) { | ||
| return NextResponse.json({ error: "未授权访问" }, { status: 401 }); | ||
| } | ||
|
|
||
| // 验证环境变量 | ||
| if ( | ||
| !process.env.R2_ACCOUNT_ID || | ||
| !process.env.R2_ACCESS_KEY_ID || | ||
| !process.env.R2_SECRET_ACCESS_KEY || | ||
| !process.env.R2_BUCKET_NAME || | ||
| !process.env.R2_PUBLIC_URL | ||
| ) { | ||
| console.error("R2 环境变量未配置"); | ||
| return NextResponse.json( | ||
| { error: "服务器配置错误:R2 未配置" }, | ||
| { status: 500 }, | ||
| ); | ||
| } | ||
|
|
||
| // 解析请求体 | ||
| const body = (await request.json()) as UploadRequest; | ||
| const { filename, contentType, articleSlug } = body; | ||
|
|
||
| // 验证请求参数 | ||
| if (!filename || !contentType || !articleSlug) { | ||
| return NextResponse.json( | ||
| { error: "缺少必要参数:filename, contentType, articleSlug" }, | ||
| { status: 400 }, | ||
| ); | ||
| } | ||
|
|
||
Crokily marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // 验证文件类型 | ||
| if (!contentType.startsWith("image/")) { | ||
| return NextResponse.json( | ||
| { error: "仅支持图片类型文件" }, | ||
| { status: 400 }, | ||
| ); | ||
| } | ||
|
|
||
| // 生成唯一的对象键 | ||
| // 格式:users/{userId}/{article-slug}/{timestamp}-{filename} | ||
| const timestamp = Date.now(); | ||
| const userId = session.user.id; | ||
| const sanitizedSlug = sanitizeDocumentSlug(articleSlug); | ||
| const sanitizedFilename = sanitizeResourceKey(filename); | ||
| const key = `users/${userId}/${sanitizedSlug}/${timestamp}-${sanitizedFilename}`; | ||
|
|
||
| // 创建 PutObject 命令 | ||
| const command = new PutObjectCommand({ | ||
| Bucket: process.env.R2_BUCKET_NAME, | ||
| Key: key, | ||
| ContentType: contentType, | ||
| }); | ||
|
|
||
| // 生成预签名 URL(15 分钟有效期) | ||
| const uploadUrl = await getSignedUrl(r2Client, command, { | ||
| expiresIn: 900, | ||
| }); | ||
|
|
||
| // 生成公开访问 URL | ||
| const publicUrl = `${process.env.R2_PUBLIC_URL}/${key}`; | ||
|
|
||
| return NextResponse.json({ | ||
| uploadUrl, | ||
| publicUrl, | ||
| key, | ||
| }); | ||
| } catch (error) { | ||
| console.error("生成预签名 URL 失败:", error); | ||
| return NextResponse.json( | ||
| { | ||
| error: "生成上传链接失败", | ||
| details: error instanceof Error ? error.message : "未知错误", | ||
| }, | ||
| { status: 500 }, | ||
| ); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.