feat(web): 관리자 CRM 웹페이지 — admin role + 4개 페이지
- 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 네비게이션 추가
This commit is contained in:
parent
d3c2a4348d
commit
667c09242b
10 changed files with 816 additions and 0 deletions
32
apps/web/src/lib/admin-guard.ts
Normal file
32
apps/web/src/lib/admin-guard.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue