- migration: profiles.role 컬럼 + admin RLS 정책 - admin-guard.ts: RSC용 admin 권한 체크 - /admin: CRM 대시보드 (총 유저, 유료 구독자, API 호출, 만료 예정) - /admin/users: 유저 목록 (검색/필터/페이지네이션) - /admin/users/[id]: 유저 상세 (프로필+구독+30일 사용량) - /admin/subscriptions: 구독 목록 (상태 필터) - /admin/usage: 사용량 집계 (7/14/30일) - Sidebar에 Admin 네비게이션 추가
32 lines
783 B
TypeScript
32 lines
783 B
TypeScript
// apps/web/src/lib/admin-guard.ts
|
|
// RSC용 admin 가드 — profile.role='admin' 체크, 실패 시 redirect
|
|
|
|
import { redirect } from 'next/navigation'
|
|
import { getSupabaseServerClient } from './supabase-server'
|
|
|
|
interface AdminProfile {
|
|
id: string
|
|
name: string | null
|
|
role: string
|
|
}
|
|
|
|
export async function requireAdmin(): Promise<AdminProfile> {
|
|
const supabase = await getSupabaseServerClient()
|
|
const { data: { user } } = await supabase.auth.getUser()
|
|
|
|
if (!user) {
|
|
redirect('/login')
|
|
}
|
|
|
|
const { data: profile } = await supabase
|
|
.from('profiles')
|
|
.select('id, name, role')
|
|
.eq('id', user.id)
|
|
.maybeSingle()
|
|
|
|
if (!profile || (profile as { role: string }).role !== 'admin') {
|
|
redirect('/dashboard')
|
|
}
|
|
|
|
return profile as AdminProfile
|
|
}
|