feat: V2-7 Admin 콘솔 리디자인 + 3단계 권한 + SaaS 전환 + 코드 정리
- Admin CRM: D3RO Console 스타일 전체 적용 (panelSx/tableSx/filterBtnSx) - 3단계 권한: manager/admin/super_admin (DB + Edge Functions + Frontend) - 랜딩 페이지: 1회 결제 → 월간/연간 구독 SaaS 모델 (10개 언어) - SSE 스트리밍: VoiceConversation Premium LLM 라우팅 + fallback - Supabase 클라이언트: packages/api-client 공통 추출 (browser+server) - RPC 함수 타입: 9개 정의 (admin_usage_by_feature 등) - callAdminApi 401 버그 수정 (getUser() 선행 토큰 갱신)
This commit is contained in:
parent
d0e854c255
commit
8af75a0a1e
50 changed files with 2185 additions and 950 deletions
|
|
@ -1,20 +1,43 @@
|
|||
// server/supabase/functions/_shared/admin-auth.ts
|
||||
// Admin/Super-admin 권한 검증 — requireUser 확장
|
||||
// 3단계 권한 검증: manager < admin < super_admin
|
||||
|
||||
// @ts-expect-error — Deno 런타임 import
|
||||
import type { User } from 'https://esm.sh/@supabase/supabase-js@2.39.7'
|
||||
import { requireUser, type AuthError } from './auth.ts'
|
||||
|
||||
export type AdminRole = 'admin' | 'super_admin'
|
||||
export type AdminRole = 'manager' | 'admin' | 'super_admin'
|
||||
|
||||
const ROLE_LEVEL: Record<string, number> = {
|
||||
user: 0,
|
||||
manager: 1,
|
||||
admin: 2,
|
||||
super_admin: 3,
|
||||
}
|
||||
|
||||
function getUserRole(user: User): string {
|
||||
return ((user.app_metadata as Record<string, unknown>)?.role as string) ?? 'user'
|
||||
}
|
||||
|
||||
/**
|
||||
* manager 이상 권한 필요 (manager, admin, super_admin).
|
||||
*/
|
||||
export async function requireManager(req: Request): Promise<User> {
|
||||
const user = await requireUser(req)
|
||||
const role = getUserRole(user)
|
||||
if ((ROLE_LEVEL[role] ?? 0) < ROLE_LEVEL.manager) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||
throw { status: 403, message: 'Manager access required' } as AuthError
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
/**
|
||||
* admin 이상 권한 필요 (admin, super_admin).
|
||||
* 실패 시 AuthError throw.
|
||||
*/
|
||||
export async function requireAdmin(req: Request): Promise<User> {
|
||||
const user = await requireUser(req)
|
||||
const role = (user.app_metadata as Record<string, unknown>)?.role as string | undefined
|
||||
if (role !== 'admin' && role !== 'super_admin') {
|
||||
const role = getUserRole(user)
|
||||
if ((ROLE_LEVEL[role] ?? 0) < ROLE_LEVEL.admin) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||
throw { status: 403, message: 'Admin access required' } as AuthError
|
||||
}
|
||||
|
|
@ -23,12 +46,11 @@ export async function requireAdmin(req: Request): Promise<User> {
|
|||
|
||||
/**
|
||||
* super_admin 전용 권한 필요.
|
||||
* 실패 시 AuthError throw.
|
||||
*/
|
||||
export async function requireSuperAdmin(req: Request): Promise<User> {
|
||||
const user = await requireUser(req)
|
||||
const role = (user.app_metadata as Record<string, unknown>)?.role as string | undefined
|
||||
if (role !== 'super_admin') {
|
||||
const role = getUserRole(user)
|
||||
if ((ROLE_LEVEL[role] ?? 0) < ROLE_LEVEL.super_admin) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||
throw { status: 403, message: 'Super admin access required' } as AuthError
|
||||
}
|
||||
|
|
@ -36,10 +58,18 @@ export async function requireSuperAdmin(req: Request): Promise<User> {
|
|||
}
|
||||
|
||||
/**
|
||||
* 현재 유저의 admin role 반환. admin이 아니면 null.
|
||||
* 현재 유저의 admin role 반환. manager 미만이면 null.
|
||||
*/
|
||||
export function getAdminRole(user: User): AdminRole | null {
|
||||
const role = (user.app_metadata as Record<string, unknown>)?.role as string | undefined
|
||||
if (role === 'admin' || role === 'super_admin') return role
|
||||
const role = getUserRole(user)
|
||||
if (role === 'manager' || role === 'admin' || role === 'super_admin') return role as AdminRole
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 유저가 특정 최소 레벨 이상인지 체크.
|
||||
*/
|
||||
export function hasMinRole(user: User, minRole: AdminRole): boolean {
|
||||
const role = getUserRole(user)
|
||||
return (ROLE_LEVEL[role] ?? 0) >= (ROLE_LEVEL[minRole] ?? 0)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue