From 539946f5a364b94a0edd55de7ed3e0fb477a7966 Mon Sep 17 00:00:00 2001 From: ideaswork Date: Thu, 9 Jan 2025 15:07:04 +0800 Subject: [PATCH 1/2] bugfix --- .env | 1 + app/layout.tsx | 1 - middleware.ts | 124 +++++++++++++----------------------------------- next.config.mjs | 3 +- 4 files changed, 37 insertions(+), 92 deletions(-) diff --git a/.env b/.env index 81a504d..c142969 100644 --- a/.env +++ b/.env @@ -8,3 +8,4 @@ DATABASE_URL="file:./dev.db" JWT_SECRET=ideaswork +NEXT_PUBLIC_BASE_URL=http://127.0.0.1:3000 \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index 29f0b41..0457886 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,5 @@ import type { Metadata } from "next"; -import localFont from "next/font/local"; import { Toaster } from "@/components/ui/toaster" import Navbar from '@/components/Navbar' import Footer from '@/components/Footer' diff --git a/middleware.ts b/middleware.ts index ef22439..aebacac 100644 --- a/middleware.ts +++ b/middleware.ts @@ -2,121 +2,65 @@ import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { verifyToken } from '@/lib/auth' -// Public paths that don't require authentication -const publicPaths = Object.freeze([ +const publicPaths = new Set([ '/login', '/register', '/', - '/documentation(.*)', // Match all documentation paths and subpaths - '/api/auth/login', - '/api/auth/register', - '/api/auth/me', // Allow all auth endpoints + '/documentation', + '/api/auth', '/api/md', - '/workspace', + '/workspace', '/unauthorized', - '/api/(.*)', // Match all API paths and subpaths using regex pattern - '/worklog/user', + '/worklog/user' ]) -const protectedRoutes = [ - { path: '/', roles: ['Team Member', 'Scrum Master', 'Product Owner'] }, - { path: '/workspace', roles: ['Team Member', 'Scrum Master', 'Product Owner'] }, - { path: '/dashboard', roles: ['Team Member', 'Scrum Master', 'Product Owner'] }, - { path: '/task', roles: ['Team Member', 'Scrum Master', 'Product Owner'] }, - { path: '/worklog/user', roles: ['Team Member', 'Scrum Master', 'Product Owner'] }, - { path: '/documentation', roles: ['Team Member', 'Scrum Master', 'Product Owner'] }, - { path: '/product', roles: ['Scrum Master'] }, - { path: '/sprint', roles: ['Scrum Master'] }, - { path: '/team', roles: ['Scrum Master'] }, - { path: '/userstory', roles: ['Product Owner', 'Scrum Master'] }, - { path: '/backlog', roles: ['Product Owner', 'Scrum Master'] }, - { path: '/worklog/admin', roles: ['Scrum Master'] }, -]; +type Role = 'Team Member' | 'Scrum Master' | 'Product Owner' +type RoleAccess = Record -export async function middleware(request: NextRequest) { - const url = new URL(request.url) - let path = url.pathname - - // Normalize path - path = path.replace(/\/+/g, '/').replace(/\/$/, '') || '/' - - console.log(`[Middleware] Request path: ${path}`) +const roleAccess: RoleAccess = { + 'Team Member': ['/', '/workspace', '/dashboard', '/task', '/worklog/user', '/documentation'], + 'Scrum Master': ['/product', '/sprint', '/team', '/userstory', '/backlog', '/worklog/admin'], + 'Product Owner': ['/userstory', '/backlog'] +} - const isPublicPath = (publicPath: string) => { - if (publicPath.includes('(.*)')) { - const regex = new RegExp(`^${publicPath.replace('(.*)', '.*')}$`) - return regex.test(path) - } - return path === publicPath || path.startsWith(publicPath + '/') - } +export async function middleware(request: NextRequest) { + const { pathname } = new URL(request.url) + const path = pathname.replace(/\/+/g, '/').replace(/\/$/, '') || '/' - // Allow public paths - if (publicPaths.some(isPublicPath)) { - console.log(`[Middleware] Public path accessed: ${path}`) + // Check public paths + const publicPathsArray = Array.from(publicPaths) + if (publicPathsArray.some((p: string) => path === p || path.startsWith(p + '/'))) { return NextResponse.next() } - // Get token from cookies + // Verify token const token = request.cookies.get('token')?.value - console.log(`[Middleware] Token found: ${!!token}`) - - // Verify JWT token - let payload = null + let payload try { payload = token ? await verifyToken(token) : null - console.log(`[Middleware] Token payload:`, payload) - } catch (error) { - console.error(`[Middleware] Token verification error:`, error) - if (!path.startsWith('/login')) { - return NextResponse.redirect(new URL('/login', request.url)) - } - return NextResponse.next() + } catch { + return path.startsWith('/login') ? NextResponse.next() : NextResponse.redirect(new URL('/login', request.url)) } - // Redirect to login if not authenticated if (!payload) { - console.log(`[Middleware] Unauthenticated request, redirecting to login`) - if (!path.startsWith('/login')) { - return NextResponse.redirect(new URL('/login', request.url)) - } - return NextResponse.next() + return path.startsWith('/login') ? NextResponse.next() : NextResponse.redirect(new URL('/login', request.url)) } - // Add user info to request headers - const requestHeaders = new Headers(request.headers) - requestHeaders.set('x-user-id', payload.user_id) - requestHeaders.set('x-user-role', payload.role) + // Set user headers + const headers = new Headers(request.headers) + headers.set('x-user-id', payload.user_id) + headers.set('x-user-role', payload.role) - // Get allowed paths based on user role - const allowedPaths = protectedRoutes - .filter(route => route.roles.includes(payload.role)) - .map(route => route.path) - - // Check if current path is allowed - const isPathAllowed = allowedPaths.some(allowedPath => - path === allowedPath || path.startsWith(allowedPath + '/') - ) - - if (!isPathAllowed) { - console.log(`[Middleware] Access denied for role ${payload.role} to path ${path}`) - console.log(`Allowed paths for role ${payload.role}:`, allowedPaths) - if (!path.startsWith('/unauthorized')) { - return NextResponse.redirect(new URL('/unauthorized', request.url)) - } - return NextResponse.next() + // Check role access + const role = payload.role as Role + const allowedPaths = roleAccess[role] || [] + if (!allowedPaths.some((p: string) => path === p || path.startsWith(p + '/'))) { + return path.startsWith('/unauthorized') ? NextResponse.next() : NextResponse.redirect(new URL('/unauthorized', request.url)) } - // Continue with authenticated request - console.log(`[Middleware] Proceeding with authenticated request`) - return NextResponse.next({ - request: { - headers: requestHeaders - } - }) + return NextResponse.next({ request: { headers } }) } export const config = { - matcher: [ - '/((?!_next/static|_next/image|favicon.ico|api/|static/).*)', // 确保排除所有静态资源 - ], + matcher: ['/((?!_next/static|_next/image|favicon.ico|api/|static/).*)'] } \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index ff9ec9b..514c846 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,7 +1,8 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - assetPrefix: process.env.NODE_ENV === 'production' ? '' : '', + assetPrefix: process.env.NEXT_PUBLIC_BASE_URL, basePath: '', + trailingSlash: false, // 禁用尾部斜杠 experimental: { forceSwcTransforms: true, }, -- Gitee From 2c7794de111f340369dc05fed15faae210e14b33 Mon Sep 17 00:00:00 2001 From: ideaswork Date: Thu, 9 Jan 2025 15:07:30 +0800 Subject: [PATCH 2/2] bugfix --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index c142969..a5aa304 100644 --- a/.env +++ b/.env @@ -8,4 +8,4 @@ DATABASE_URL="file:./dev.db" JWT_SECRET=ideaswork -NEXT_PUBLIC_BASE_URL=http://127.0.0.1:3000 \ No newline at end of file +NEXT_PUBLIC_BASE_URL=http://129.226.159.129:3000 \ No newline at end of file -- Gitee