Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ INTERN_KEY=
# 智谱 AI 开放平台 API Key,聊天与建议接口的默认免费模型 GLM-4.6V-Flash
# 在 https://open.bigmodel.cn/ 注册后获取
ZHIPU_API_KEY=

# Sentry 错误监控(Developer plan 免费 5K errors / 10K perf units / 月)
# NEXT_PUBLIC_SENTRY_DSN 是浏览器端需要的公开 DSN,暴露在前端 bundle 里属于设计,
# SENTRY_AUTH_TOKEN 仅用于 next build 时上传 source map,私密。
# SDK 只在 NODE_ENV=production 启用,本地 dev/preview 不会产生事件、不耗配额。
# Vercel 端由 Sentry 集成自动注入;本地 dev 可不填。
Comment on lines +36 to +37
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.env.sample 里写“本地 dev/preview 不会产生事件”,但当前实现只用 NODE_ENV === "production" 控制;在 Vercel Preview 部署中 NODE_ENV 同样是 production,实际上仍可能上报事件并消耗配额。建议在示例说明里补充真实条件(例如结合 VERCEL_ENV),或同步调整代码使其与文档一致。

Suggested change
# SDK 只在 NODE_ENV=production 启用,本地 dev/preview 不会产生事件、不耗配额。
# Vercel 端由 Sentry 集成自动注入;本地 dev 可不填。
# 当前是否上报事件取决于代码中的启用条件;若仅判断 NODE_ENV=production,
# 则 Vercel Preview(其 NODE_ENV 通常也是 production)也可能上报并消耗配额。
# 如需仅在正式生产环境上报,代码中应额外结合 VERCEL_ENV=production 等条件。
# Vercel 端可由 Sentry 集成自动注入;本地 dev 可不填。

Copilot uses AI. Check for mistakes.
NEXT_PUBLIC_SENTRY_DSN=
SENTRY_AUTH_TOKEN=
SENTRY_ORG=involutionhell
SENTRY_PROJECT=sentry-bole-notebook
# Neon 项目 ID
NEON_PROJECT_ID=
# Neon 提供的 Postgres 连接。
Expand Down
18 changes: 18 additions & 0 deletions instrumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Next.js 15+ 约定的启动 hook:每个 runtime 启动时各自拉对应 Sentry 配置,
* 避免在 edge runtime 错误加载 Node-only 代码。
*
* onRequestError 暴露给 Next.js 捕获服务端组件/路由层面的未捕获错误。
*/
import * as Sentry from "@sentry/nextjs";

export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
await import("./sentry.server.config");
}
if (process.env.NEXT_RUNTIME === "edge") {
await import("./sentry.edge.config");
}
}

export const onRequestError = Sentry.captureRequestError;
25 changes: 24 additions & 1 deletion next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// next.config.mjs
import { createMDX } from "fumadocs-mdx/next";
import createNextIntlPlugin from "next-intl/plugin";
import { withSentryConfig } from "@sentry/nextjs";

/**
* IMPORTANT: remarkImage 配置已移至 source.config.ts 统一管理
Expand Down Expand Up @@ -131,4 +132,26 @@ const config = {
},
};

export default withNextIntl(withMDX(config));
const finalConfig = withNextIntl(withMDX(config));

// Sentry 包裹:webpack 插件需要看到最终的 Next 配置才能上传 source map。
// silent: !CI 让本地构建不刷日志,只在 Vercel CI 构建时打印。
// widenClientFileUpload 扩大 source map 扫描范围,保证前端错误堆栈能解出来。
// disableLogger 树摇掉 Sentry 自带 logger,减小 bundle 体积。
//
// 守门条件:只有在存在 SENTRY_AUTH_TOKEN 时才启用 withSentryConfig。
// 贡献者 clone 仓库后没配 Sentry env 也能直接 `pnpm build` / `pnpm dev`,
// 不会因为 webpack 插件缺凭据而构建失败。生产 Vercel 那边 env 齐全,正常上报。
const enableSentry = Boolean(process.env.SENTRY_AUTH_TOKEN);

export default enableSentry
? withSentryConfig(finalConfig, {
org: process.env.SENTRY_ORG || "involutionhell",
project: process.env.SENTRY_PROJECT || "sentry-bole-notebook",
Comment on lines +142 to +150
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enableSentry 只检查 SENTRY_AUTH_TOKEN,但 org/project 采用硬编码 fallback。这样在本地/CI 里只要误设置了 token(但没设 org/project)就可能把 sourcemap 上传到默认项目,产生混淆或污染错误归因。建议把启用条件改为同时要求 SENTRY_AUTH_TOKEN && SENTRY_ORG && SENTRY_PROJECT,或去掉 fallback 并在缺失时显式失败/跳过上传。

Suggested change
// 守门条件:只有在存在 SENTRY_AUTH_TOKEN 时才启用 withSentryConfig。
// 贡献者 clone 仓库后没配 Sentry env 也能直接 `pnpm build` / `pnpm dev`,
// 不会因为 webpack 插件缺凭据而构建失败。生产 Vercel 那边 env 齐全,正常上报。
const enableSentry = Boolean(process.env.SENTRY_AUTH_TOKEN);
export default enableSentry
? withSentryConfig(finalConfig, {
org: process.env.SENTRY_ORG || "involutionhell",
project: process.env.SENTRY_PROJECT || "sentry-bole-notebook",
// 守门条件:只有在存在完整的 Sentry env 时才启用 withSentryConfig。
// 贡献者 clone 仓库后没配 Sentry env 也能直接 `pnpm build` / `pnpm dev`,
// 不会因为 webpack 插件缺凭据而构建失败。生产 Vercel 那边 env 齐全,正常上报。
const enableSentry = Boolean(
process.env.SENTRY_AUTH_TOKEN &&
process.env.SENTRY_ORG &&
process.env.SENTRY_PROJECT,
);
export default enableSentry
? withSentryConfig(finalConfig, {
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,

Copilot uses AI. Check for mistakes.
silent: !process.env.CI,
widenClientFileUpload: true,
disableLogger: true,
// 不启用 tunnelRoute:需要加 /monitoring rewrite,和现有 rewrites 交互
// 复杂;广告屏蔽对 docs 站影响小,后续真需要再打开。
})
: finalConfig;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tooltip": "^1.2.8",
"@sentry/nextjs": "^10.49.0",
"@types/mdx": "^2.0.13",
"@types/pg": "^8.16.0",
"@vercel/speed-insights": "^1.2.0",
Expand Down
1,416 changes: 1,384 additions & 32 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions sentry.client.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Sentry 浏览器端初始化。
*
* 免费 tier 策略(Developer plan,5K errors/月 + 10K perf units/月):
* - 只在 production 启用,避免本地 dev/preview 污染配额
* - tracesSampleRate 0.1:10% 的页面 transaction 采样足够看性能趋势
* - 关闭 Session Replay:它是另外的独立配额(小),开了容易炸
* - 不启用 profiling(需要付费)
*/
import * as Sentry from "@sentry/nextjs";

Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
enabled: process.env.NODE_ENV === "production",
Comment on lines +12 to +14
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enabled 目前只依赖 NODE_ENV === "production",但在 Vercel Preview/本地 next build 场景里 NODE_ENV 也可能是 production,导致预览环境也会上报并消耗配额。建议把启用条件改成同时校验部署环境(例如 process.env.VERCEL_ENV === "production")并且 DSN 存在(!!process.env.NEXT_PUBLIC_SENTRY_DSN),以避免误上报/SDK 初始化告警。

Suggested change
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
enabled: process.env.NODE_ENV === "production",
const isSentryEnabled =
process.env.NODE_ENV === "production" &&
process.env.VERCEL_ENV === "production" &&
!!process.env.NEXT_PUBLIC_SENTRY_DSN;
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
enabled: isSentryEnabled,

Copilot uses AI. Check for mistakes.
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0,
replaysOnErrorSampleRate: 0,
// 线上开 false 省日志;排障时临时改 true
debug: false,
});
12 changes: 12 additions & 0 deletions sentry.edge.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Sentry Edge runtime 初始化(Middleware / Edge routes)。
* 当前项目未使用 edge runtime,保留配置以便未来迁移时无需补齐。
*/
import * as Sentry from "@sentry/nextjs";

Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
enabled: process.env.NODE_ENV === "production",
Comment on lines +3 to +9
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

文件头注释说“当前项目未使用 edge runtime”,但仓库里已有 middleware.ts(默认运行在 Edge runtime),因此这段说明不准确且可能误导后续维护。建议更新注释,说明该配置用于 middleware/edge routes,并明确启用策略(例如是否仅 production + DSN 存在时启用)。

Suggested change
* 当前项目未使用 edge runtime,保留配置以便未来迁移时无需补齐。
*/
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
enabled: process.env.NODE_ENV === "production",
* 当前配置用于 Next.js middleware 及其他 Edge runtime 入口。
* 仅在 production 且已配置 NEXT_PUBLIC_SENTRY_DSN 时启用,避免本地/未配置环境误报。
*/
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
enabled:
process.env.NODE_ENV === "production" &&
Boolean(process.env.NEXT_PUBLIC_SENTRY_DSN),

Copilot uses AI. Check for mistakes.
tracesSampleRate: 0.1,
debug: false,
});
12 changes: 12 additions & 0 deletions sentry.server.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Sentry Node.js runtime 初始化(Next.js API routes / Server Components / RSC)。
* 与 client 同策略:仅 production 启用,traces 10%,无 replay/profiling。
*/
import * as Sentry from "@sentry/nextjs";

Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
enabled: process.env.NODE_ENV === "production",
Comment on lines +7 to +9
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里同样建议在 enabled 上额外校验 DSN 是否存在(避免 production 下 DSN 漏配时 SDK 初始化产生告警/无意义开销),并结合实际部署环境(如 Vercel 的 VERCEL_ENV)区分 production vs preview,避免预览环境也上报占用配额。

Suggested change
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
enabled: process.env.NODE_ENV === "production",
const dsn = process.env.NEXT_PUBLIC_SENTRY_DSN;
const isProductionDeployment =
process.env.VERCEL_ENV != null
? process.env.VERCEL_ENV === "production"
: process.env.NODE_ENV === "production";
Sentry.init({
dsn,
enabled: Boolean(dsn) && isProductionDeployment,

Copilot uses AI. Check for mistakes.
tracesSampleRate: 0.1,
debug: false,
});
Loading