repo_description: 使用 NextJS + Notion API 实现的，支持多种部署方案的静态博客，无需服务器、零门槛搭建网站，为Notion和所有创作者设计。 (A static blog built with NextJS and Notion API, supporting multiple deployment options. No server required, zero threshold to set up a website. Designed for Notion and all creators.)
repo_topics: blog nextjs notion react tailwindcss vercel zeabur
code: import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
  import { NextRequest, NextResponse } from 'next/server'
  import { checkStrIsNotionId, getLastPartOfUrl } from '@/lib/utils'
  import { idToUuid } from 'notion-utils'
  import BLOG from './blog.config'
  
  /**
   * Clerk 身份验证中间件
   */
  export const config = {
    // 这里设置白名单，防止静态资源被拦截
    matcher: ['/((?!.*\\..*|_next|/sign-in|/auth).*)', '/', '/(api|trpc)(.*)']
  }
  
  // 限制登录访问的路由
  const isTenantRoute = createRouteMatcher([
    '/user/organization-selector(.*)',
    '/user/orgid/(.*)',
    '/dashboard',
    '/dashboard/(.*)'
  ])
  
  // 限制权限访问的路由
  const isTenantAdminRoute = createRouteMatcher([
    '/admin/(.*)/memberships',
    '/admin/(.*)/domain'
  ])
  
  /**
   * 没有配置权限相关功能的返回
   * @param req
   * @param ev
   * @returns
   */
  // eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
  const noAuthMiddleware = async (req: NextRequest, ev: any) => {
    // 如果没有配置 Clerk 相关环境变量，返回一个默认响应或者继续处理请求
    if (BLOG['UUID_REDIRECT']) {
      let redirectJson: Record<string, string> = {}
      try {
        const response = await fetch(`${req.nextUrl.origin}/redirect.json`)
        if (response.ok) {
          redirectJson = (await response.json()) as Record<string, string>
        }
      } catch (err) {
        console.error('Error fetching static file:', err)
      }
      let lastPart = getLastPartOfUrl(req.nextUrl.pathname) as string
      if (checkStrIsNotionId(lastPart)) {
        lastPart = idToUuid(lastPart)
      }
      if (lastPart && redirectJson[lastPart]) {
        const redirectToUrl = req.nextUrl.clone()
        redirectToUrl.pathname = '/' + redirectJson[lastPart]
        console.log(
          `redirect from ${req.nextUrl.pathname} to ${redirectToUrl.pathname}`
        )
        return NextResponse.redirect(redirectToUrl, 308)
      }
    }
    return NextResponse.next()
  }
  /**
   * 鉴权中间件
   */
  const authMiddleware = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
    ? clerkMiddleware((auth, req) => {
        const { userId } = auth()
        // 处理 /dashboard 路由的登录保护
        if (isTenantRoute(req)) {
          if (!userId) {
            // 用户未登录，重定向到 /sign-in
            const url = new URL('/sign-in', req.url)
            url.searchParams.set('redirectTo', req.url) // 保存重定向目标
            return NextResponse.redirect(url)
          }
        }
  
        // 处理管理员相关权限保护
        if (isTenantAdminRoute(req)) {
          auth().protect(has => {
            return (
              has({ permission: 'org:sys_memberships:manage' }) ||
              has({ permission: 'org:sys_domains_manage' })
            )
          })
        }
  
        // 默认继续处理请求
        return NextResponse.next()
      })
    : noAuthMiddleware
  
  export default authMiddleware
  