-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmiddleware.ts
More file actions
219 lines (185 loc) · 7.6 KB
/
middleware.ts
File metadata and controls
219 lines (185 loc) · 7.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
import { createMiddlewareClient } from './lib/supabase'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// Supported locales
export const locales = ['pt', 'en'] as const
export const defaultLocale = 'pt' as const
export type Locale = typeof locales[number]
// Admin configuration
const ADMIN_EMAIL = 'armazemsaojoaquimoficial@gmail.com'
// Admin verification function (similar to lib/admin-auth.ts but for middleware)
async function verifyAdminAccessMiddleware(supabase: any, session: any): Promise<boolean> {
try {
const user = session?.user
if (!user) return false
// Primary verification: Check by admin email (fastest)
const adminEmail = 'armazemsaojoaquimoficial@gmail.com'
if (user.email === adminEmail) {
console.log('✅ [MIDDLEWARE] Admin verified by email:', user.email)
return true
}
// Fallback verification: Check role in profiles table
try {
const { data: profile, error } = await supabase
.from('profiles')
.select('role')
.eq('id', user.id)
.single()
if (!error && profile?.role === 'admin') {
console.log('✅ [MIDDLEWARE] Admin verified by role in database')
return true
}
console.log('🔍 [MIDDLEWARE] Profile check result:', { profile, error: error?.message })
} catch (dbError) {
console.warn('⚠️ [MIDDLEWARE] Database verification failed:', dbError)
}
console.log('❌ [MIDDLEWARE] User is not admin:', user.email)
return false
} catch (error) {
console.error('❌ [MIDDLEWARE] Error in admin verification:', error)
return false
}
}
// Get locale from pathname
function getLocale(pathname: string): Locale | null {
const segments = pathname.split('/')
const firstSegment = segments[1] as Locale
return locales.includes(firstSegment) ? firstSegment : null
}
// Check if path needs locale redirect
function needsLocaleRedirect(pathname: string): boolean {
// Skip API routes, static files, and certain paths
if (
pathname.startsWith('/api/') ||
pathname.startsWith('/_next/') ||
pathname.startsWith('/favicon') ||
pathname.startsWith('/manifest') ||
pathname.startsWith('/robots') ||
pathname.startsWith('/sitemap') ||
pathname.includes('.')
) {
return false
}
// Check if pathname already has a locale
return !locales.some(locale =>
pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
)
}
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl
// Skip static files
if (
pathname.startsWith('/_next/') ||
pathname.startsWith('/favicon') ||
pathname.startsWith('/manifest') ||
pathname.startsWith('/robots') ||
pathname.startsWith('/sitemap') ||
pathname.includes('.')
) {
return NextResponse.next()
}
// Handle locale redirects (skip for API routes)
if (!pathname.startsWith('/api/') && needsLocaleRedirect(pathname)) {
if (pathname === '/') {
return NextResponse.redirect(new URL(`/${defaultLocale}`, request.url))
}
return NextResponse.redirect(new URL(`/${defaultLocale}${pathname}`, request.url))
}
// Extract locale
const locale = getLocale(pathname)
const pathWithoutLocale = pathname.replace(`/${locale}`, '') || '/'
try {
const response = NextResponse.next()
// Add security headers
response.headers.set('X-Frame-Options', 'DENY')
response.headers.set('X-Content-Type-Options', 'nosniff')
response.headers.set('X-XSS-Protection', '1; mode=block')
// Create Supabase client for auth handling
const supabase = createMiddlewareClient(request, response)
// Admin routes protection
if (pathWithoutLocale.startsWith('/admin') || pathname.startsWith('/api/admin/')) {
console.log('🔒 MIDDLEWARE: Admin route accessed:', pathname)
// Get session
let session = null
try {
const { data: { session: userSession } } = await supabase.auth.getSession()
session = userSession
// Try Authorization header if no session from cookies
if (!session && request.headers.get('authorization')) {
const authHeader = request.headers.get('authorization')
if (authHeader?.startsWith('Bearer ')) {
const token = authHeader.split(' ')[1]
const { createClient } = await import('@supabase/supabase-js')
const tokenClient = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
const { data: { user } } = await tokenClient.auth.getUser(token)
if (user) {
session = { user, access_token: token }
}
}
}
} catch (authError) {
console.warn('⚠️ MIDDLEWARE: Auth error:', authError)
}
// No session - redirect to auth or return 401 for API routes
if (!session?.user) {
if (pathname.startsWith('/api/admin/')) {
return NextResponse.json(
{ error: 'Authentication required', code: 'NO_SESSION' },
{ status: 401 }
)
}
const redirectUrl = request.nextUrl.clone()
redirectUrl.pathname = `/${locale}/auth`
redirectUrl.searchParams.set('message', 'É necessário fazer login para acessar o painel administrativo')
redirectUrl.searchParams.set('redirect', pathname)
return NextResponse.redirect(redirectUrl)
}
console.log('🔍 [MIDDLEWARE] Checking admin access for:', session.user.email)
const isAdmin = await verifyAdminAccessMiddleware(supabase, session)
if (!isAdmin) {
if (pathname.startsWith('/api/admin/')) {
return NextResponse.json(
{ error: 'Access denied - admin only', code: 'ACCESS_DENIED' },
{ status: 403 }
)
}
const redirectUrl = request.nextUrl.clone()
redirectUrl.pathname = `/${locale}/unauthorized`
redirectUrl.searchParams.set('message', 'Acesso negado - apenas administradores podem acessar esta área')
return NextResponse.redirect(redirectUrl)
}
// Add admin session headers
response.headers.set('X-Admin-Session', 'true')
response.headers.set('X-Admin-Verified', 'admin_email')
}
return response
} catch (error: any) {
console.error('❌ MIDDLEWARE: Critical error:', error)
// For admin routes with error, redirect to auth or return error
if (pathWithoutLocale.startsWith('/admin') || pathname.startsWith('/api/admin/')) {
if (pathname.startsWith('/api/admin/')) {
return NextResponse.json(
{ error: 'Internal server error', code: 'MIDDLEWARE_ERROR' },
{ status: 500 }
)
}
const redirectUrl = request.nextUrl.clone()
redirectUrl.pathname = `/${locale || defaultLocale}/auth`
redirectUrl.searchParams.set('error', 'middleware_error')
redirectUrl.searchParams.set('message', 'Erro na verificação de autenticação. Tente novamente.')
return NextResponse.redirect(redirectUrl)
}
// Basic response for non-admin routes
const response = NextResponse.next()
response.headers.set('X-Frame-Options', 'DENY')
return response
}
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon|manifest|robots|sitemap|.*\\..*).*)',
],
}