feat: complete release preparation, 10+ ad mediation, CI/CD, and docker deployment
Some checks failed
CI Pipeline / Code Quality & Typecheck (push) Waiting to run
CI Pipeline / Test Suite (macos-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (ubuntu-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (windows-latest) (push) Blocked by required conditions
CI Pipeline / Build Validation (admin) (push) Blocked by required conditions
CI Pipeline / Build Validation (desktop) (push) Blocked by required conditions
Deploy Landing Page / deploy (push) Blocked by required conditions
Deploy Landing Page / build (push) Waiting to run
Release & Packaging Pipeline / Build & Publish Admin Docker Image (push) Failing after 8s
Release & Code Signing CA Pipeline / build-and-sign-windows (push) Failing after 1m51s
Build macOS / Build & Package (macOS) (push) Failing after 4s
Build macOS / Build & Package (macOS)-1 (push) Failing after 5s
Release & Code Signing CA Pipeline / build-and-sign-macos (push) Failing after 3s
Release & Packaging Pipeline / Package macOS Desktop App (push) Failing after 4s
Release & Packaging Pipeline / Package Windows Desktop App (push) Failing after 2m28s
Release & Packaging Pipeline / Publish Official GitHub Release (push) Has been skipped
Some checks failed
CI Pipeline / Code Quality & Typecheck (push) Waiting to run
CI Pipeline / Test Suite (macos-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (ubuntu-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (windows-latest) (push) Blocked by required conditions
CI Pipeline / Build Validation (admin) (push) Blocked by required conditions
CI Pipeline / Build Validation (desktop) (push) Blocked by required conditions
Deploy Landing Page / deploy (push) Blocked by required conditions
Deploy Landing Page / build (push) Waiting to run
Release & Packaging Pipeline / Build & Publish Admin Docker Image (push) Failing after 8s
Release & Code Signing CA Pipeline / build-and-sign-windows (push) Failing after 1m51s
Build macOS / Build & Package (macOS) (push) Failing after 4s
Build macOS / Build & Package (macOS)-1 (push) Failing after 5s
Release & Code Signing CA Pipeline / build-and-sign-macos (push) Failing after 3s
Release & Packaging Pipeline / Package macOS Desktop App (push) Failing after 4s
Release & Packaging Pipeline / Package Windows Desktop App (push) Failing after 2m28s
Release & Packaging Pipeline / Publish Official GitHub Release (push) Has been skipped
This commit is contained in:
parent
5cd1de6859
commit
708e20f747
406 changed files with 42464 additions and 6199 deletions
|
|
@ -1,37 +1,70 @@
|
|||
// apps/admin/src/middleware.ts
|
||||
// Supabase 세션 갱신 미들웨어 — 모든 요청에서 쿠키 기반 세션을 갱신
|
||||
// D3RO Voice — Industrial Grade Admin Route & Security Guard
|
||||
|
||||
import { NextResponse, type NextRequest } from 'next/server'
|
||||
import { createServerClient, type CookieOptions } from '@supabase/ssr'
|
||||
|
||||
const PUBLIC_PATHS = ['/login', '/auth/callback', '/api/auth/login', '/api/auth/logout', '/favicon.ico', '/robots.txt']
|
||||
|
||||
export async function middleware(request: NextRequest): Promise<NextResponse> {
|
||||
const { pathname } = request.nextUrl
|
||||
|
||||
// 1. Check if path is public (e.g. login, static assets)
|
||||
const isPublic = PUBLIC_PATHS.some((path) => pathname === path || pathname.startsWith(path + '/'))
|
||||
const isStatic = pathname.startsWith('/_next') || pathname.startsWith('/static') || pathname.includes('.')
|
||||
|
||||
// 2. Validate session cookie
|
||||
const sessionCookie = request.cookies.get('d3ro_admin_session')?.value
|
||||
let isAuthenticated = false
|
||||
|
||||
if (sessionCookie) {
|
||||
try {
|
||||
const decoded = JSON.parse(Buffer.from(sessionCookie, 'base64').toString('utf-8'))
|
||||
if (decoded && decoded.expiresAt && decoded.expiresAt > Date.now()) {
|
||||
isAuthenticated = true
|
||||
}
|
||||
} catch {
|
||||
isAuthenticated = false
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Unauthenticated access to protected route -> Redirect to /login
|
||||
if (!isAuthenticated && !isPublic && !isStatic) {
|
||||
const loginUrl = new URL('/login', request.url)
|
||||
if (pathname !== '/') {
|
||||
loginUrl.searchParams.set('redirect', pathname)
|
||||
}
|
||||
const redirectResponse = NextResponse.redirect(loginUrl)
|
||||
addSecurityHeaders(redirectResponse)
|
||||
return redirectResponse
|
||||
}
|
||||
|
||||
// 4. Authenticated user visiting /login -> Redirect to Dashboard /
|
||||
if (isAuthenticated && pathname === '/login') {
|
||||
const dashboardUrl = new URL('/', request.url)
|
||||
const redirectResponse = NextResponse.redirect(dashboardUrl)
|
||||
addSecurityHeaders(redirectResponse)
|
||||
return redirectResponse
|
||||
}
|
||||
|
||||
// 5. Proceed with Security Headers attached
|
||||
const response = NextResponse.next({ request: { headers: request.headers } })
|
||||
|
||||
const url = process.env.NEXT_PUBLIC_SUPABASE_URL
|
||||
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
|
||||
|
||||
if (!url || !key) return response
|
||||
|
||||
const supabase = createServerClient(url, key, {
|
||||
cookies: {
|
||||
getAll() {
|
||||
return request.cookies.getAll()
|
||||
},
|
||||
setAll(cookiesToSet: Array<{ name: string; value: string; options: CookieOptions }>) {
|
||||
cookiesToSet.forEach(({ name, value, options }) => {
|
||||
request.cookies.set({ name, value, ...options })
|
||||
response.cookies.set({ name, value, ...options })
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// 세션 갱신 (토큰 리프레시)
|
||||
await supabase.auth.getUser()
|
||||
|
||||
addSecurityHeaders(response)
|
||||
return response
|
||||
}
|
||||
|
||||
function addSecurityHeaders(response: NextResponse): void {
|
||||
// Anti-Crawling & Anti-Reconnaissance (Shodan, Google, Bing, AI scrapers)
|
||||
response.headers.set('X-Robots-Tag', 'noindex, nofollow, noarchive, nosnippet, noimageindex')
|
||||
// Clickjacking Prevention
|
||||
response.headers.set('X-Frame-Options', 'DENY')
|
||||
// MIME Sniffing Prevention
|
||||
response.headers.set('X-Content-Type-Options', 'nosniff')
|
||||
// Referrer Privacy
|
||||
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')
|
||||
// Feature Policy
|
||||
response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()')
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue